| author | |
| committer | |
| log | 9a5a1013a833229e1d12588615c1a05644f76cc5 |
| tree | c8a1cca2e5324891a4f748b3fd38c0f65ef78d03 |
| parent | c12a262503150d1b2251f7ba29e0f1ba4f649bf6 |
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.6 files changed, 220 insertions(+), 109 deletions(-)
lib/std/zig/ast.zig+80-12| ... | ... | @@ -526,6 +526,7 @@ pub const Node = struct { |
| 526 | 526 | Comptime, |
| 527 | 527 | Nosuspend, |
| 528 | 528 | Block, |
| 529 | LabeledBlock, | |
| 529 | 530 | |
| 530 | 531 | // Misc |
| 531 | 532 | DocComment, |
| ... | ... | @@ -654,6 +655,7 @@ pub const Node = struct { |
| 654 | 655 | .Comptime => Comptime, |
| 655 | 656 | .Nosuspend => Nosuspend, |
| 656 | 657 | .Block => Block, |
| 658 | .LabeledBlock => LabeledBlock, | |
| 657 | 659 | .DocComment => DocComment, |
| 658 | 660 | .SwitchCase => SwitchCase, |
| 659 | 661 | .SwitchElse => SwitchElse, |
| ... | ... | @@ -666,6 +668,13 @@ pub const Node = struct { |
| 666 | 668 | .FieldInitializer => FieldInitializer, |
| 667 | 669 | }; |
| 668 | 670 | } |
| 671 | ||
| 672 | pub fn isBlock(tag: Tag) bool { | |
| 673 | return switch (tag) { | |
| 674 | .Block, .LabeledBlock => true, | |
| 675 | else => false, | |
| 676 | }; | |
| 677 | } | |
| 669 | 678 | }; |
| 670 | 679 | |
| 671 | 680 | /// Prefer `castTag` to this. |
| ... | ... | @@ -729,6 +738,7 @@ pub const Node = struct { |
| 729 | 738 | .Root, |
| 730 | 739 | .ContainerField, |
| 731 | 740 | .Block, |
| 741 | .LabeledBlock, | |
| 732 | 742 | .Payload, |
| 733 | 743 | .PointerPayload, |
| 734 | 744 | .PointerIndexPayload, |
| ... | ... | @@ -739,6 +749,7 @@ pub const Node = struct { |
| 739 | 749 | .DocComment, |
| 740 | 750 | .TestDecl, |
| 741 | 751 | => return false, |
| 752 | ||
| 742 | 753 | .While => { |
| 743 | 754 | const while_node = @fieldParentPtr(While, "base", n); |
| 744 | 755 | if (while_node.@"else") |@"else"| { |
| ... | ... | @@ -746,7 +757,7 @@ pub const Node = struct { |
| 746 | 757 | continue; |
| 747 | 758 | } |
| 748 | 759 | |
| 749 | return while_node.body.tag != .Block; | |
| 760 | return !while_node.body.tag.isBlock(); | |
| 750 | 761 | }, |
| 751 | 762 | .For => { |
| 752 | 763 | const for_node = @fieldParentPtr(For, "base", n); |
| ... | ... | @@ -755,7 +766,7 @@ pub const Node = struct { |
| 755 | 766 | continue; |
| 756 | 767 | } |
| 757 | 768 | |
| 758 | return for_node.body.tag != .Block; | |
| 769 | return !for_node.body.tag.isBlock(); | |
| 759 | 770 | }, |
| 760 | 771 | .If => { |
| 761 | 772 | const if_node = @fieldParentPtr(If, "base", n); |
| ... | ... | @@ -764,7 +775,7 @@ pub const Node = struct { |
| 764 | 775 | continue; |
| 765 | 776 | } |
| 766 | 777 | |
| 767 | return if_node.body.tag != .Block; | |
| 778 | return !if_node.body.tag.isBlock(); | |
| 768 | 779 | }, |
| 769 | 780 | .Else => { |
| 770 | 781 | const else_node = @fieldParentPtr(Else, "base", n); |
| ... | ... | @@ -773,29 +784,40 @@ pub const Node = struct { |
| 773 | 784 | }, |
| 774 | 785 | .Defer => { |
| 775 | 786 | const defer_node = @fieldParentPtr(Defer, "base", n); |
| 776 | return defer_node.expr.tag != .Block; | |
| 787 | return !defer_node.expr.tag.isBlock(); | |
| 777 | 788 | }, |
| 778 | 789 | .Comptime => { |
| 779 | 790 | const comptime_node = @fieldParentPtr(Comptime, "base", n); |
| 780 | return comptime_node.expr.tag != .Block; | |
| 791 | return !comptime_node.expr.tag.isBlock(); | |
| 781 | 792 | }, |
| 782 | 793 | .Suspend => { |
| 783 | 794 | const suspend_node = @fieldParentPtr(Suspend, "base", n); |
| 784 | 795 | if (suspend_node.body) |body| { |
| 785 | return body.tag != .Block; | |
| 796 | return !body.tag.isBlock(); | |
| 786 | 797 | } |
| 787 | 798 | |
| 788 | 799 | return true; |
| 789 | 800 | }, |
| 790 | 801 | .Nosuspend => { |
| 791 | 802 | const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n); |
| 792 | return nosuspend_node.expr.tag != .Block; | |
| 803 | return !nosuspend_node.expr.tag.isBlock(); | |
| 793 | 804 | }, |
| 794 | 805 | else => return true, |
| 795 | 806 | } |
| 796 | 807 | } |
| 797 | 808 | } |
| 798 | 809 | |
| 810 | /// Asserts the node is a Block or LabeledBlock and returns the statements slice. | |
| 811 | pub fn blockStatements(base: *Node) []*Node { | |
| 812 | if (base.castTag(.Block)) |block| { | |
| 813 | return block.statements(); | |
| 814 | } else if (base.castTag(.LabeledBlock)) |labeled_block| { | |
| 815 | return labeled_block.statements(); | |
| 816 | } else { | |
| 817 | unreachable; | |
| 818 | } | |
| 819 | } | |
| 820 | ||
| 799 | 821 | pub fn dump(self: *Node, indent: usize) void { |
| 800 | 822 | { |
| 801 | 823 | var i: usize = 0; |
| ... | ... | @@ -1460,7 +1482,6 @@ pub const Node = struct { |
| 1460 | 1482 | statements_len: NodeIndex, |
| 1461 | 1483 | lbrace: TokenIndex, |
| 1462 | 1484 | rbrace: TokenIndex, |
| 1463 | label: ?TokenIndex, | |
| 1464 | 1485 | |
| 1465 | 1486 | /// After this the caller must initialize the statements list. |
| 1466 | 1487 | pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*Block { |
| ... | ... | @@ -1483,10 +1504,6 @@ pub const Node = struct { |
| 1483 | 1504 | } |
| 1484 | 1505 | |
| 1485 | 1506 | pub fn firstToken(self: *const Block) TokenIndex { |
| 1486 | if (self.label) |label| { | |
| 1487 | return label; | |
| 1488 | } | |
| 1489 | ||
| 1490 | 1507 | return self.lbrace; |
| 1491 | 1508 | } |
| 1492 | 1509 | |
| ... | ... | @@ -1509,6 +1526,57 @@ pub const Node = struct { |
| 1509 | 1526 | } |
| 1510 | 1527 | }; |
| 1511 | 1528 | |
| 1529 | /// The statements of the block follow LabeledBlock directly in memory. | |
| 1530 | pub const LabeledBlock = struct { | |
| 1531 | base: Node = Node{ .tag = .LabeledBlock }, | |
| 1532 | statements_len: NodeIndex, | |
| 1533 | lbrace: TokenIndex, | |
| 1534 | rbrace: TokenIndex, | |
| 1535 | label: TokenIndex, | |
| 1536 | ||
| 1537 | /// After this the caller must initialize the statements list. | |
| 1538 | pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*LabeledBlock { | |
| 1539 | const bytes = try allocator.alignedAlloc(u8, @alignOf(LabeledBlock), sizeInBytes(statements_len)); | |
| 1540 | return @ptrCast(*LabeledBlock, bytes.ptr); | |
| 1541 | } | |
| 1542 | ||
| 1543 | pub fn free(self: *LabeledBlock, allocator: *mem.Allocator) void { | |
| 1544 | const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.statements_len)]; | |
| 1545 | allocator.free(bytes); | |
| 1546 | } | |
| 1547 | ||
| 1548 | pub fn iterate(self: *const LabeledBlock, index: usize) ?*Node { | |
| 1549 | var i = index; | |
| 1550 | ||
| 1551 | if (i < self.statements_len) return self.statementsConst()[i]; | |
| 1552 | i -= self.statements_len; | |
| 1553 | ||
| 1554 | return null; | |
| 1555 | } | |
| 1556 | ||
| 1557 | pub fn firstToken(self: *const LabeledBlock) TokenIndex { | |
| 1558 | return self.label; | |
| 1559 | } | |
| 1560 | ||
| 1561 | pub fn lastToken(self: *const LabeledBlock) TokenIndex { | |
| 1562 | return self.rbrace; | |
| 1563 | } | |
| 1564 | ||
| 1565 | pub fn statements(self: *LabeledBlock) []*Node { | |
| 1566 | const decls_start = @ptrCast([*]u8, self) + @sizeOf(LabeledBlock); | |
| 1567 | return @ptrCast([*]*Node, decls_start)[0..self.statements_len]; | |
| 1568 | } | |
| 1569 | ||
| 1570 | pub fn statementsConst(self: *const LabeledBlock) []const *Node { | |
| 1571 | const decls_start = @ptrCast([*]const u8, self) + @sizeOf(LabeledBlock); | |
| 1572 | return @ptrCast([*]const *Node, decls_start)[0..self.statements_len]; | |
| 1573 | } | |
| 1574 | ||
| 1575 | fn sizeInBytes(statements_len: NodeIndex) usize { | |
| 1576 | return @sizeOf(LabeledBlock) + @sizeOf(*Node) * @as(usize, statements_len); | |
| 1577 | } | |
| 1578 | }; | |
| 1579 | ||
| 1512 | 1580 | pub const Defer = struct { |
| 1513 | 1581 | base: Node = Node{ .tag = .Defer }, |
| 1514 | 1582 | defer_token: TokenIndex, |
lib/std/zig/parse.zig+37-29| ... | ... | @@ -364,9 +364,10 @@ const Parser = struct { |
| 364 | 364 | const name_node = try p.expectNode(parseStringLiteralSingle, .{ |
| 365 | 365 | .ExpectedStringLiteral = .{ .token = p.tok_i }, |
| 366 | 366 | }); |
| 367 | const block_node = try p.expectNode(parseBlock, .{ | |
| 368 | .ExpectedLBrace = .{ .token = p.tok_i }, | |
| 369 | }); | |
| 367 | const block_node = (try p.parseBlock(null)) orelse { | |
| 368 | try p.errors.append(p.gpa, .{ .ExpectedLBrace = .{ .token = p.tok_i } }); | |
| 369 | return error.ParseError; | |
| 370 | }; | |
| 370 | 371 | |
| 371 | 372 | const test_node = try p.arena.allocator.create(Node.TestDecl); |
| 372 | 373 | test_node.* = .{ |
| ... | ... | @@ -540,12 +541,14 @@ const Parser = struct { |
| 540 | 541 | if (p.eatToken(.Semicolon)) |_| { |
| 541 | 542 | break :blk null; |
| 542 | 543 | } |
| 543 | break :blk try p.expectNodeRecoverable(parseBlock, .{ | |
| 544 | const body_block = (try p.parseBlock(null)) orelse { | |
| 544 | 545 | // Since parseBlock only return error.ParseError on |
| 545 | 546 | // a missing '}' we can assume this function was |
| 546 | 547 | // supposed to end here. |
| 547 | .ExpectedSemiOrLBrace = .{ .token = p.tok_i }, | |
| 548 | }); | |
| 548 | try p.errors.append(p.gpa, .{ .ExpectedSemiOrLBrace = .{ .token = p.tok_i } }); | |
| 549 | break :blk null; | |
| 550 | }; | |
| 551 | break :blk body_block; | |
| 549 | 552 | }, |
| 550 | 553 | .as_type => null, |
| 551 | 554 | }; |
| ... | ... | @@ -823,10 +826,7 @@ const Parser = struct { |
| 823 | 826 | var colon: TokenIndex = undefined; |
| 824 | 827 | const label_token = p.parseBlockLabel(&colon); |
| 825 | 828 | |
| 826 | if (try p.parseBlock()) |node| { | |
| 827 | node.cast(Node.Block).?.label = label_token; | |
| 828 | return node; | |
| 829 | } | |
| 829 | if (try p.parseBlock(label_token)) |node| return node; | |
| 830 | 830 | |
| 831 | 831 | if (try p.parseLoopStatement()) |node| { |
| 832 | 832 | if (node.cast(Node.For)) |for_node| { |
| ... | ... | @@ -1003,14 +1003,13 @@ const Parser = struct { |
| 1003 | 1003 | fn parseBlockExpr(p: *Parser) Error!?*Node { |
| 1004 | 1004 | var colon: TokenIndex = undefined; |
| 1005 | 1005 | const label_token = p.parseBlockLabel(&colon); |
| 1006 | const block_node = (try p.parseBlock()) orelse { | |
| 1006 | const block_node = (try p.parseBlock(label_token)) orelse { | |
| 1007 | 1007 | if (label_token) |label| { |
| 1008 | 1008 | p.putBackToken(label + 1); // ":" |
| 1009 | 1009 | p.putBackToken(label); // IDENTIFIER |
| 1010 | 1010 | } |
| 1011 | 1011 | return null; |
| 1012 | 1012 | }; |
| 1013 | block_node.cast(Node.Block).?.label = label_token; | |
| 1014 | 1013 | return block_node; |
| 1015 | 1014 | } |
| 1016 | 1015 | |
| ... | ... | @@ -1177,7 +1176,7 @@ const Parser = struct { |
| 1177 | 1176 | p.putBackToken(token); // IDENTIFIER |
| 1178 | 1177 | } |
| 1179 | 1178 | |
| 1180 | if (try p.parseBlock()) |node| return node; | |
| 1179 | if (try p.parseBlock(null)) |node| return node; | |
| 1181 | 1180 | if (try p.parseCurlySuffixExpr()) |node| return node; |
| 1182 | 1181 | |
| 1183 | 1182 | return null; |
| ... | ... | @@ -1189,7 +1188,7 @@ const Parser = struct { |
| 1189 | 1188 | } |
| 1190 | 1189 | |
| 1191 | 1190 | /// Block <- LBRACE Statement* RBRACE |
| 1192 | fn parseBlock(p: *Parser) !?*Node { | |
| 1191 | fn parseBlock(p: *Parser, label_token: ?TokenIndex) !?*Node { | |
| 1193 | 1192 | const lbrace = p.eatToken(.LBrace) orelse return null; |
| 1194 | 1193 | |
| 1195 | 1194 | var statements = std.ArrayList(*Node).init(p.gpa); |
| ... | ... | @@ -1211,16 +1210,26 @@ const Parser = struct { |
| 1211 | 1210 | |
| 1212 | 1211 | const statements_len = @intCast(NodeIndex, statements.items.len); |
| 1213 | 1212 | |
| 1214 | const block_node = try Node.Block.alloc(&p.arena.allocator, statements_len); | |
| 1215 | block_node.* = .{ | |
| 1216 | .label = null, | |
| 1217 | .lbrace = lbrace, | |
| 1218 | .statements_len = statements_len, | |
| 1219 | .rbrace = rbrace, | |
| 1220 | }; | |
| 1221 | std.mem.copy(*Node, block_node.statements(), statements.items); | |
| 1222 | ||
| 1223 | return &block_node.base; | |
| 1213 | if (label_token) |label| { | |
| 1214 | const block_node = try Node.LabeledBlock.alloc(&p.arena.allocator, statements_len); | |
| 1215 | block_node.* = .{ | |
| 1216 | .label = label, | |
| 1217 | .lbrace = lbrace, | |
| 1218 | .statements_len = statements_len, | |
| 1219 | .rbrace = rbrace, | |
| 1220 | }; | |
| 1221 | std.mem.copy(*Node, block_node.statements(), statements.items); | |
| 1222 | return &block_node.base; | |
| 1223 | } else { | |
| 1224 | const block_node = try Node.Block.alloc(&p.arena.allocator, statements_len); | |
| 1225 | block_node.* = .{ | |
| 1226 | .lbrace = lbrace, | |
| 1227 | .statements_len = statements_len, | |
| 1228 | .rbrace = rbrace, | |
| 1229 | }; | |
| 1230 | std.mem.copy(*Node, block_node.statements(), statements.items); | |
| 1231 | return &block_node.base; | |
| 1232 | } | |
| 1224 | 1233 | } |
| 1225 | 1234 | |
| 1226 | 1235 | /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) |
| ... | ... | @@ -1658,11 +1667,8 @@ const Parser = struct { |
| 1658 | 1667 | var colon: TokenIndex = undefined; |
| 1659 | 1668 | const label = p.parseBlockLabel(&colon); |
| 1660 | 1669 | |
| 1661 | if (label) |token| { | |
| 1662 | if (try p.parseBlock()) |node| { | |
| 1663 | node.cast(Node.Block).?.label = token; | |
| 1664 | return node; | |
| 1665 | } | |
| 1670 | if (label) |label_token| { | |
| 1671 | if (try p.parseBlock(label_token)) |node| return node; | |
| 1666 | 1672 | } |
| 1667 | 1673 | |
| 1668 | 1674 | if (try p.parseLoopTypeExpr()) |node| { |
| ... | ... | @@ -3440,6 +3446,7 @@ const Parser = struct { |
| 3440 | 3446 | } |
| 3441 | 3447 | } |
| 3442 | 3448 | |
| 3449 | /// TODO Delete this function. I don't like the inversion of control. | |
| 3443 | 3450 | fn expectNode( |
| 3444 | 3451 | p: *Parser, |
| 3445 | 3452 | parseFn: NodeParseFn, |
| ... | ... | @@ -3449,6 +3456,7 @@ const Parser = struct { |
| 3449 | 3456 | return (try p.expectNodeRecoverable(parseFn, err)) orelse return error.ParseError; |
| 3450 | 3457 | } |
| 3451 | 3458 | |
| 3459 | /// TODO Delete this function. I don't like the inversion of control. | |
| 3452 | 3460 | fn expectNodeRecoverable( |
| 3453 | 3461 | p: *Parser, |
| 3454 | 3462 | parseFn: NodeParseFn, |
lib/std/zig/render.zig+31-8| ... | ... | @@ -392,28 +392,50 @@ fn renderExpression( |
| 392 | 392 | return renderToken(tree, stream, any_type.token, indent, start_col, space); |
| 393 | 393 | }, |
| 394 | 394 | |
| 395 | .Block => { | |
| 396 | const block = @fieldParentPtr(ast.Node.Block, "base", base); | |
| 395 | .Block, .LabeledBlock => { | |
| 396 | const block: struct { | |
| 397 | label: ?ast.TokenIndex, | |
| 398 | statements: []*ast.Node, | |
| 399 | lbrace: ast.TokenIndex, | |
| 400 | rbrace: ast.TokenIndex, | |
| 401 | } = b: { | |
| 402 | if (base.castTag(.Block)) |block| { | |
| 403 | break :b .{ | |
| 404 | .label = null, | |
| 405 | .statements = block.statements(), | |
| 406 | .lbrace = block.lbrace, | |
| 407 | .rbrace = block.rbrace, | |
| 408 | }; | |
| 409 | } else if (base.castTag(.LabeledBlock)) |block| { | |
| 410 | break :b .{ | |
| 411 | .label = block.label, | |
| 412 | .statements = block.statements(), | |
| 413 | .lbrace = block.lbrace, | |
| 414 | .rbrace = block.rbrace, | |
| 415 | }; | |
| 416 | } else { | |
| 417 | unreachable; | |
| 418 | } | |
| 419 | }; | |
| 397 | 420 | |
| 398 | 421 | if (block.label) |label| { |
| 399 | 422 | try renderToken(tree, stream, label, indent, start_col, Space.None); |
| 400 | 423 | try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); |
| 401 | 424 | } |
| 402 | 425 | |
| 403 | if (block.statements_len == 0) { | |
| 426 | if (block.statements.len == 0) { | |
| 404 | 427 | try renderToken(tree, stream, block.lbrace, indent + indent_delta, start_col, Space.None); |
| 405 | 428 | return renderToken(tree, stream, block.rbrace, indent, start_col, space); |
| 406 | 429 | } else { |
| 407 | 430 | const block_indent = indent + indent_delta; |
| 408 | 431 | try renderToken(tree, stream, block.lbrace, block_indent, start_col, Space.Newline); |
| 409 | 432 | |
| 410 | const block_statements = block.statements(); | |
| 411 | for (block_statements) |statement, i| { | |
| 433 | for (block.statements) |statement, i| { | |
| 412 | 434 | try stream.writeByteNTimes(' ', block_indent); |
| 413 | 435 | try renderStatement(allocator, stream, tree, block_indent, start_col, statement); |
| 414 | 436 | |
| 415 | if (i + 1 < block_statements.len) { | |
| 416 | try renderExtraNewline(tree, stream, start_col, block_statements[i + 1]); | |
| 437 | if (i + 1 < block.statements.len) { | |
| 438 | try renderExtraNewline(tree, stream, start_col, block.statements[i + 1]); | |
| 417 | 439 | } |
| 418 | 440 | } |
| 419 | 441 | |
| ... | ... | @@ -1841,7 +1863,7 @@ fn renderExpression( |
| 1841 | 1863 | |
| 1842 | 1864 | const rparen = tree.nextToken(for_node.array_expr.lastToken()); |
| 1843 | 1865 | |
| 1844 | const body_is_block = for_node.body.tag == .Block; | |
| 1866 | const body_is_block = for_node.body.tag.isBlock(); | |
| 1845 | 1867 | const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken()); |
| 1846 | 1868 | const body_on_same_line = body_is_block or src_one_line_to_body; |
| 1847 | 1869 | |
| ... | ... | @@ -2578,6 +2600,7 @@ fn renderDocCommentsToken( |
| 2578 | 2600 | fn nodeIsBlock(base: *const ast.Node) bool { |
| 2579 | 2601 | return switch (base.tag) { |
| 2580 | 2602 | .Block, |
| 2603 | .LabeledBlock, | |
| 2581 | 2604 | .If, |
| 2582 | 2605 | .For, |
| 2583 | 2606 | .While, |
src-self-hosted/Module.zig+1-1| ... | ... | @@ -1343,7 +1343,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1343 | 1343 | |
| 1344 | 1344 | const body_block = body_node.cast(ast.Node.Block).?; |
| 1345 | 1345 | |
| 1346 | _ = try astgen.blockExpr(self, params_scope, .none, body_block); | |
| 1346 | try astgen.blockExpr(self, params_scope, body_block); | |
| 1347 | 1347 | |
| 1348 | 1348 | if (gen_scope.instructions.items.len == 0 or |
| 1349 | 1349 | !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) |
src-self-hosted/astgen.zig+25-15| ... | ... | @@ -107,31 +107,46 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 107 | 107 | .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)), |
| 108 | 108 | .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)), |
| 109 | 109 | .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?), |
| 110 | .Block => return blockExpr(mod, scope, rl, node.castTag(.Block).?), | |
| 110 | .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)), | |
| 111 | .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?), | |
| 111 | 112 | else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}), |
| 112 | 113 | } |
| 113 | 114 | } |
| 114 | 115 | |
| 115 | pub fn blockExpr( | |
| 116 | pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void { | |
| 117 | const tracy = trace(@src()); | |
| 118 | defer tracy.end(); | |
| 119 | ||
| 120 | try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements()); | |
| 121 | } | |
| 122 | ||
| 123 | fn labeledBlockExpr( | |
| 116 | 124 | mod: *Module, |
| 117 | 125 | parent_scope: *Scope, |
| 118 | 126 | rl: ResultLoc, |
| 119 | block_node: *ast.Node.Block, | |
| 127 | block_node: *ast.Node.LabeledBlock, | |
| 120 | 128 | ) InnerError!*zir.Inst { |
| 121 | 129 | const tracy = trace(@src()); |
| 122 | 130 | defer tracy.end(); |
| 123 | 131 | |
| 124 | if (block_node.label) |label| { | |
| 125 | return mod.failTok(parent_scope, label, "TODO implement labeled blocks", .{}); | |
| 132 | const statements = block_node.statements(); | |
| 133 | ||
| 134 | if (statements.len == 0) { | |
| 135 | // Hot path for `{}`. | |
| 136 | return rlWrapVoid(mod, parent_scope, rl, &block_node.base, {}); | |
| 126 | 137 | } |
| 127 | 138 | |
| 128 | var block_arena = std.heap.ArenaAllocator.init(mod.gpa); | |
| 129 | defer block_arena.deinit(); | |
| 139 | return mod.failNode(parent_scope, &block_node.base, "TODO implement labeled blocks", .{}); | |
| 140 | } | |
| 130 | 141 | |
| 142 | fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void { | |
| 131 | 143 | const tree = parent_scope.tree(); |
| 132 | 144 | |
| 145 | var block_arena = std.heap.ArenaAllocator.init(mod.gpa); | |
| 146 | defer block_arena.deinit(); | |
| 147 | ||
| 133 | 148 | var scope = parent_scope; |
| 134 | for (block_node.statements()) |statement| { | |
| 149 | for (statements) |statement| { | |
| 135 | 150 | const src = tree.token_locs[statement.firstToken()].start; |
| 136 | 151 | _ = try addZIRNoOp(mod, scope, src, .dbg_stmt); |
| 137 | 152 | switch (statement.tag) { |
| ... | ... | @@ -162,12 +177,6 @@ pub fn blockExpr( |
| 162 | 177 | }, |
| 163 | 178 | } |
| 164 | 179 | } |
| 165 | ||
| 166 | const src = tree.token_locs[block_node.firstToken()].start; | |
| 167 | return addZIRInstConst(mod, parent_scope, src, .{ | |
| 168 | .ty = Type.initTag(.void), | |
| 169 | .val = Value.initTag(.void_value), | |
| 170 | }); | |
| 171 | 180 | } |
| 172 | 181 | |
| 173 | 182 | fn varDecl( |
| ... | ... | @@ -1184,6 +1193,7 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool { |
| 1184 | 1193 | .Slice, |
| 1185 | 1194 | .Deref, |
| 1186 | 1195 | .ArrayAccess, |
| 1196 | .Block, | |
| 1187 | 1197 | => return false, |
| 1188 | 1198 | |
| 1189 | 1199 | // Forward the question to a sub-expression. |
| ... | ... | @@ -1210,11 +1220,11 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool { |
| 1210 | 1220 | .Switch, |
| 1211 | 1221 | .Call, |
| 1212 | 1222 | .BuiltinCall, // TODO some of these can return false |
| 1223 | .LabeledBlock, | |
| 1213 | 1224 | => return true, |
| 1214 | 1225 | |
| 1215 | 1226 | // Depending on AST properties, they may need memory locations. |
| 1216 | 1227 | .If => return node.castTag(.If).?.@"else" != null, |
| 1217 | .Block => return node.castTag(.Block).?.label != null, | |
| 1218 | 1228 | } |
| 1219 | 1229 | } |
| 1220 | 1230 | } |
src-self-hosted/translate_c.zig+46-44| ... | ... | @@ -118,19 +118,31 @@ const Scope = struct { |
| 118 | 118 | self.* = undefined; |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | fn complete(self: *Block, c: *Context) !*ast.Node.Block { | |
| 121 | fn complete(self: *Block, c: *Context) !*ast.Node { | |
| 122 | 122 | // We reserve 1 extra statement if the parent is a Loop. This is in case of |
| 123 | 123 | // do while, we want to put `if (cond) break;` at the end. |
| 124 | 124 | const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop); |
| 125 | const node = try ast.Node.Block.alloc(c.arena, alloc_len); | |
| 126 | node.* = .{ | |
| 127 | .statements_len = self.statements.items.len, | |
| 128 | .lbrace = self.lbrace, | |
| 129 | .rbrace = try appendToken(c, .RBrace, "}"), | |
| 130 | .label = self.label, | |
| 131 | }; | |
| 132 | mem.copy(*ast.Node, node.statements(), self.statements.items); | |
| 133 | return node; | |
| 125 | const rbrace = try appendToken(c, .RBrace, "}"); | |
| 126 | if (self.label) |label| { | |
| 127 | const node = try ast.Node.LabeledBlock.alloc(c.arena, alloc_len); | |
| 128 | node.* = .{ | |
| 129 | .statements_len = self.statements.items.len, | |
| 130 | .lbrace = self.lbrace, | |
| 131 | .rbrace = rbrace, | |
| 132 | .label = label, | |
| 133 | }; | |
| 134 | mem.copy(*ast.Node, node.statements(), self.statements.items); | |
| 135 | return &node.base; | |
| 136 | } else { | |
| 137 | const node = try ast.Node.Block.alloc(c.arena, alloc_len); | |
| 138 | node.* = .{ | |
| 139 | .statements_len = self.statements.items.len, | |
| 140 | .lbrace = self.lbrace, | |
| 141 | .rbrace = rbrace, | |
| 142 | }; | |
| 143 | mem.copy(*ast.Node, node.statements(), self.statements.items); | |
| 144 | return &node.base; | |
| 145 | } | |
| 134 | 146 | } |
| 135 | 147 | |
| 136 | 148 | /// Given the desired name, return a name that does not shadow anything from outer scopes. |
| ... | ... | @@ -320,15 +332,9 @@ pub const Context = struct { |
| 320 | 332 | return node; |
| 321 | 333 | } |
| 322 | 334 | |
| 323 | fn createBlock(c: *Context, label: ?[]const u8, statements_len: ast.NodeIndex) !*ast.Node.Block { | |
| 324 | const label_node = if (label) |l| blk: { | |
| 325 | const ll = try appendIdentifier(c, l); | |
| 326 | _ = try appendToken(c, .Colon, ":"); | |
| 327 | break :blk ll; | |
| 328 | } else null; | |
| 335 | fn createBlock(c: *Context, statements_len: ast.NodeIndex) !*ast.Node.Block { | |
| 329 | 336 | const block_node = try ast.Node.Block.alloc(c.arena, statements_len); |
| 330 | 337 | block_node.* = .{ |
| 331 | .label = label_node, | |
| 332 | 338 | .lbrace = try appendToken(c, .LBrace, "{"), |
| 333 | 339 | .statements_len = statements_len, |
| 334 | 340 | .rbrace = undefined, |
| ... | ... | @@ -640,8 +646,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { |
| 640 | 646 | var last = block_scope.statements.items[block_scope.statements.items.len - 1]; |
| 641 | 647 | while (true) { |
| 642 | 648 | switch (last.tag) { |
| 643 | .Block => { | |
| 644 | const stmts = last.castTag(.Block).?.statements(); | |
| 649 | .Block, .LabeledBlock => { | |
| 650 | const stmts = last.blockStatements(); | |
| 645 | 651 | if (stmts.len == 0) break; |
| 646 | 652 | |
| 647 | 653 | last = stmts[stmts.len - 1]; |
| ... | ... | @@ -669,7 +675,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { |
| 669 | 675 | } |
| 670 | 676 | |
| 671 | 677 | const body_node = try block_scope.complete(rp.c); |
| 672 | proto_node.setTrailer("body_node", &body_node.base); | |
| 678 | proto_node.setTrailer("body_node", body_node); | |
| 673 | 679 | return addTopLevelDecl(c, fn_name, &proto_node.base); |
| 674 | 680 | } |
| 675 | 681 | |
| ... | ... | @@ -1275,7 +1281,7 @@ fn transStmt( |
| 1275 | 1281 | .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)), |
| 1276 | 1282 | .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)), |
| 1277 | 1283 | .NullStmtClass => { |
| 1278 | const block = try rp.c.createBlock(null, 0); | |
| 1284 | const block = try rp.c.createBlock(0); | |
| 1279 | 1285 | block.rbrace = try appendToken(rp.c, .RBrace, "}"); |
| 1280 | 1286 | return &block.base; |
| 1281 | 1287 | }, |
| ... | ... | @@ -1356,7 +1362,7 @@ fn transBinaryOperator( |
| 1356 | 1362 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); |
| 1357 | 1363 | grouped_expr.* = .{ |
| 1358 | 1364 | .lparen = lparen, |
| 1359 | .expr = &block_node.base, | |
| 1365 | .expr = block_node, | |
| 1360 | 1366 | .rparen = rparen, |
| 1361 | 1367 | }; |
| 1362 | 1368 | return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base); |
| ... | ... | @@ -1521,8 +1527,7 @@ fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompo |
| 1521 | 1527 | var block_scope = try Scope.Block.init(rp.c, scope, false); |
| 1522 | 1528 | defer block_scope.deinit(); |
| 1523 | 1529 | try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope); |
| 1524 | const node = try block_scope.complete(rp.c); | |
| 1525 | return &node.base; | |
| 1530 | return try block_scope.complete(rp.c); | |
| 1526 | 1531 | } |
| 1527 | 1532 | |
| 1528 | 1533 | fn transCStyleCastExprClass( |
| ... | ... | @@ -2589,7 +2594,7 @@ fn transDoWhileLoop( |
| 2589 | 2594 | // zig: if (!cond) break; |
| 2590 | 2595 | // zig: } |
| 2591 | 2596 | const node = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value); |
| 2592 | break :blk node.cast(ast.Node.Block).?; | |
| 2597 | break :blk node.castTag(.Block).?; | |
| 2593 | 2598 | } else blk: { |
| 2594 | 2599 | // the C statement is without a block, so we need to create a block to contain it. |
| 2595 | 2600 | // c: do |
| ... | ... | @@ -2600,7 +2605,7 @@ fn transDoWhileLoop( |
| 2600 | 2605 | // zig: if (!cond) break; |
| 2601 | 2606 | // zig: } |
| 2602 | 2607 | new = true; |
| 2603 | const block = try rp.c.createBlock(null, 2); | |
| 2608 | const block = try rp.c.createBlock(2); | |
| 2604 | 2609 | block.statements_len = 1; // over-allocated so we can add another below |
| 2605 | 2610 | block.statements()[0] = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value); |
| 2606 | 2611 | break :blk block; |
| ... | ... | @@ -2659,8 +2664,7 @@ fn transForLoop( |
| 2659 | 2664 | while_node.body = try transStmt(rp, &loop_scope, ZigClangForStmt_getBody(stmt), .unused, .r_value); |
| 2660 | 2665 | if (block_scope) |*bs| { |
| 2661 | 2666 | try bs.statements.append(&while_node.base); |
| 2662 | const node = try bs.complete(rp.c); | |
| 2663 | return &node.base; | |
| 2667 | return try bs.complete(rp.c); | |
| 2664 | 2668 | } else { |
| 2665 | 2669 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 2666 | 2670 | return &while_node.base; |
| ... | ... | @@ -2768,7 +2772,7 @@ fn transSwitch( |
| 2768 | 2772 | |
| 2769 | 2773 | const result_node = try switch_scope.pending_block.complete(rp.c); |
| 2770 | 2774 | switch_scope.pending_block.deinit(); |
| 2771 | return &result_node.base; | |
| 2775 | return result_node; | |
| 2772 | 2776 | } |
| 2773 | 2777 | |
| 2774 | 2778 | fn transCase( |
| ... | ... | @@ -2820,7 +2824,7 @@ fn transCase( |
| 2820 | 2824 | switch_scope.pending_block.deinit(); |
| 2821 | 2825 | switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false); |
| 2822 | 2826 | |
| 2823 | try switch_scope.pending_block.statements.append(&pending_node.base); | |
| 2827 | try switch_scope.pending_block.statements.append(pending_node); | |
| 2824 | 2828 | |
| 2825 | 2829 | return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value); |
| 2826 | 2830 | } |
| ... | ... | @@ -2857,7 +2861,7 @@ fn transDefault( |
| 2857 | 2861 | const pending_node = try switch_scope.pending_block.complete(rp.c); |
| 2858 | 2862 | switch_scope.pending_block.deinit(); |
| 2859 | 2863 | switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false); |
| 2860 | try switch_scope.pending_block.statements.append(&pending_node.base); | |
| 2864 | try switch_scope.pending_block.statements.append(pending_node); | |
| 2861 | 2865 | |
| 2862 | 2866 | return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value); |
| 2863 | 2867 | } |
| ... | ... | @@ -2972,7 +2976,7 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr, |
| 2972 | 2976 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); |
| 2973 | 2977 | grouped_expr.* = .{ |
| 2974 | 2978 | .lparen = lparen, |
| 2975 | .expr = &block_node.base, | |
| 2979 | .expr = block_node, | |
| 2976 | 2980 | .rparen = rparen, |
| 2977 | 2981 | }; |
| 2978 | 2982 | return maybeSuppressResult(rp, scope, used, &grouped_expr.base); |
| ... | ... | @@ -3304,7 +3308,7 @@ fn transCreatePreCrement( |
| 3304 | 3308 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); |
| 3305 | 3309 | grouped_expr.* = .{ |
| 3306 | 3310 | .lparen = try appendToken(rp.c, .LParen, "("), |
| 3307 | .expr = &block_node.base, | |
| 3311 | .expr = block_node, | |
| 3308 | 3312 | .rparen = try appendToken(rp.c, .RParen, ")"), |
| 3309 | 3313 | }; |
| 3310 | 3314 | return &grouped_expr.base; |
| ... | ... | @@ -3398,7 +3402,7 @@ fn transCreatePostCrement( |
| 3398 | 3402 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); |
| 3399 | 3403 | grouped_expr.* = .{ |
| 3400 | 3404 | .lparen = try appendToken(rp.c, .LParen, "("), |
| 3401 | .expr = &block_node.base, | |
| 3405 | .expr = block_node, | |
| 3402 | 3406 | .rparen = try appendToken(rp.c, .RParen, ")"), |
| 3403 | 3407 | }; |
| 3404 | 3408 | return &grouped_expr.base; |
| ... | ... | @@ -3589,7 +3593,7 @@ fn transCreateCompoundAssign( |
| 3589 | 3593 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); |
| 3590 | 3594 | grouped_expr.* = .{ |
| 3591 | 3595 | .lparen = try appendToken(rp.c, .LParen, "("), |
| 3592 | .expr = &block_node.base, | |
| 3596 | .expr = block_node, | |
| 3593 | 3597 | .rparen = try appendToken(rp.c, .RParen, ")"), |
| 3594 | 3598 | }; |
| 3595 | 3599 | return &grouped_expr.base; |
| ... | ... | @@ -3748,7 +3752,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const |
| 3748 | 3752 | const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); |
| 3749 | 3753 | grouped_expr.* = .{ |
| 3750 | 3754 | .lparen = lparen, |
| 3751 | .expr = &block_node.base, | |
| 3755 | .expr = block_node, | |
| 3752 | 3756 | .rparen = try appendToken(rp.c, .RParen, ")"), |
| 3753 | 3757 | }; |
| 3754 | 3758 | return maybeSuppressResult(rp, scope, used, &grouped_expr.base); |
| ... | ... | @@ -4191,7 +4195,7 @@ fn transCreateNodeAssign( |
| 4191 | 4195 | const block_node = try block_scope.complete(rp.c); |
| 4192 | 4196 | // semicolon must immediately follow rbrace because it is the last token in a block |
| 4193 | 4197 | _ = try appendToken(rp.c, .Semicolon, ";"); |
| 4194 | return &block_node.base; | |
| 4198 | return block_node; | |
| 4195 | 4199 | } |
| 4196 | 4200 | |
| 4197 | 4201 | 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 |
| 4484 | 4488 | |
| 4485 | 4489 | const block = try ast.Node.Block.alloc(c.arena, 1); |
| 4486 | 4490 | block.* = .{ |
| 4487 | .label = null, | |
| 4488 | 4491 | .lbrace = block_lbrace, |
| 4489 | 4492 | .statements_len = 1, |
| 4490 | 4493 | .rbrace = try appendToken(c, .RBrace, "}"), |
| ... | ... | @@ -5475,9 +5478,9 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { |
| 5475 | 5478 | if (last != .Eof and last != .Nl) |
| 5476 | 5479 | return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)}); |
| 5477 | 5480 | _ = try appendToken(c, .Semicolon, ";"); |
| 5478 | const type_of_arg = if (expr.tag != .Block) expr else blk: { | |
| 5479 | const blk = @fieldParentPtr(ast.Node.Block, "base", expr); | |
| 5480 | const blk_last = blk.statements()[blk.statements_len - 1]; | |
| 5481 | const type_of_arg = if (!expr.tag.isBlock()) expr else blk: { | |
| 5482 | const stmts = expr.blockStatements(); | |
| 5483 | const blk_last = stmts[stmts.len - 1]; | |
| 5481 | 5484 | const br = blk_last.cast(ast.Node.ControlFlowExpression).?; |
| 5482 | 5485 | break :blk br.getRHS().?; |
| 5483 | 5486 | }; |
| ... | ... | @@ -5500,7 +5503,7 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { |
| 5500 | 5503 | .visib_token = pub_tok, |
| 5501 | 5504 | .extern_export_inline_token = inline_tok, |
| 5502 | 5505 | .name_token = name_tok, |
| 5503 | .body_node = &block_node.base, | |
| 5506 | .body_node = block_node, | |
| 5504 | 5507 | }); |
| 5505 | 5508 | mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items); |
| 5506 | 5509 | |
| ... | ... | @@ -5555,8 +5558,7 @@ fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { |
| 5555 | 5558 | |
| 5556 | 5559 | const break_node = try transCreateNodeBreak(c, block_scope.label, last); |
| 5557 | 5560 | try block_scope.statements.append(&break_node.base); |
| 5558 | const block_node = try block_scope.complete(c); | |
| 5559 | return &block_node.base; | |
| 5561 | return try block_scope.complete(c); | |
| 5560 | 5562 | }, |
| 5561 | 5563 | else => { |
| 5562 | 5564 | m.i -= 1; |