authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-14 09:24:43+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-07-14 09:24:43+00:00
log67273cbe7618253bffa56298b5bea0e1dd37dfc2
tree7da05590dbc3b7f19a4cafdf1496a266bd4621a0
parent03f14c3102ccd3e1811ccf4fb7cae11a75d3018f
parenta92990f99312c946b5e527517a27a67a5a5513c0
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5868 from ziglang/stage2-improvements

Stage2 improvements

12 files changed, 1010 insertions(+), 464 deletions(-)

lib/std/zig/ast.zig+169-71
......@@ -410,7 +410,20 @@ pub const Node = struct {
410410
411411 // Operators
412412 InfixOp,
413 PrefixOp,
413 AddressOf,
414 Await,
415 BitNot,
416 BoolNot,
417 OptionalType,
418 Negation,
419 NegationWrap,
420 Resume,
421 Try,
422 ArrayType,
423 /// ArrayType but has a sentinel node.
424 ArrayTypeSentinel,
425 PtrType,
426 SliceType,
414427 /// Not all suffix operations are under this tag. To save memory, some
415428 /// suffix operations have dedicated Node tags.
416429 SuffixOp,
......@@ -1797,85 +1810,116 @@ pub const Node = struct {
17971810 }
17981811 };
17991812
1800 pub const PrefixOp = struct {
1801 base: Node = Node{ .id = .PrefixOp },
1813 pub const AddressOf = SimplePrefixOp(.AddressOf);
1814 pub const Await = SimplePrefixOp(.Await);
1815 pub const BitNot = SimplePrefixOp(.BitNot);
1816 pub const BoolNot = SimplePrefixOp(.BoolNot);
1817 pub const OptionalType = SimplePrefixOp(.OptionalType);
1818 pub const Negation = SimplePrefixOp(.Negation);
1819 pub const NegationWrap = SimplePrefixOp(.NegationWrap);
1820 pub const Resume = SimplePrefixOp(.Resume);
1821 pub const Try = SimplePrefixOp(.Try);
1822
1823 pub fn SimplePrefixOp(comptime tag: Id) type {
1824 return struct {
1825 base: Node = Node{ .id = tag },
1826 op_token: TokenIndex,
1827 rhs: *Node,
1828
1829 const Self = @This();
1830
1831 pub fn iterate(self: *const Self, index: usize) ?*Node {
1832 if (index == 0) return self.rhs;
1833 return null;
1834 }
1835
1836 pub fn firstToken(self: *const Self) TokenIndex {
1837 return self.op_token;
1838 }
1839
1840 pub fn lastToken(self: *const Self) TokenIndex {
1841 return self.rhs.lastToken();
1842 }
1843 };
1844 }
1845
1846 pub const ArrayType = struct {
1847 base: Node = Node{ .id = .ArrayType },
18021848 op_token: TokenIndex,
1803 op: Op,
18041849 rhs: *Node,
1850 len_expr: *Node,
18051851
1806 pub const Op = union(enum) {
1807 AddressOf,
1808 ArrayType: ArrayInfo,
1809 Await,
1810 BitNot,
1811 BoolNot,
1812 OptionalType,
1813 Negation,
1814 NegationWrap,
1815 Resume,
1816 PtrType: PtrInfo,
1817 SliceType: PtrInfo,
1818 Try,
1819 };
1852 pub fn iterate(self: *const ArrayType, index: usize) ?*Node {
1853 var i = index;
18201854
1821 pub const ArrayInfo = struct {
1822 len_expr: *Node,
1823 sentinel: ?*Node,
1824 };
1855 if (i < 1) return self.len_expr;
1856 i -= 1;
18251857
1826 pub const PtrInfo = struct {
1827 allowzero_token: ?TokenIndex = null,
1828 align_info: ?Align = null,
1829 const_token: ?TokenIndex = null,
1830 volatile_token: ?TokenIndex = null,
1831 sentinel: ?*Node = null,
1832
1833 pub const Align = struct {
1834 node: *Node,
1835 bit_range: ?BitRange,
1836
1837 pub const BitRange = struct {
1838 start: *Node,
1839 end: *Node,
1840 };
1841 };
1842 };
1858 if (i < 1) return self.rhs;
1859 i -= 1;
1860
1861 return null;
1862 }
18431863
1844 pub fn iterate(self: *const PrefixOp, index: usize) ?*Node {
1864 pub fn firstToken(self: *const ArrayType) TokenIndex {
1865 return self.op_token;
1866 }
1867
1868 pub fn lastToken(self: *const ArrayType) TokenIndex {
1869 return self.rhs.lastToken();
1870 }
1871 };
1872
1873 pub const ArrayTypeSentinel = struct {
1874 base: Node = Node{ .id = .ArrayTypeSentinel },
1875 op_token: TokenIndex,
1876 rhs: *Node,
1877 len_expr: *Node,
1878 sentinel: *Node,
1879
1880 pub fn iterate(self: *const ArrayTypeSentinel, index: usize) ?*Node {
18451881 var i = index;
18461882
1847 switch (self.op) {
1848 .PtrType, .SliceType => |addr_of_info| {
1849 if (addr_of_info.sentinel) |sentinel| {
1850 if (i < 1) return sentinel;
1851 i -= 1;
1852 }
1883 if (i < 1) return self.len_expr;
1884 i -= 1;
18531885
1854 if (addr_of_info.align_info) |align_info| {
1855 if (i < 1) return align_info.node;
1856 i -= 1;
1857 }
1858 },
1886 if (i < 1) return self.sentinel;
1887 i -= 1;
18591888
1860 .ArrayType => |array_info| {
1861 if (i < 1) return array_info.len_expr;
1862 i -= 1;
1863 if (array_info.sentinel) |sentinel| {
1864 if (i < 1) return sentinel;
1865 i -= 1;
1866 }
1867 },
1889 if (i < 1) return self.rhs;
1890 i -= 1;
18681891
1869 .AddressOf,
1870 .Await,
1871 .BitNot,
1872 .BoolNot,
1873 .OptionalType,
1874 .Negation,
1875 .NegationWrap,
1876 .Try,
1877 .Resume,
1878 => {},
1892 return null;
1893 }
1894
1895 pub fn firstToken(self: *const ArrayTypeSentinel) TokenIndex {
1896 return self.op_token;
1897 }
1898
1899 pub fn lastToken(self: *const ArrayTypeSentinel) TokenIndex {
1900 return self.rhs.lastToken();
1901 }
1902 };
1903
1904 pub const PtrType = struct {
1905 base: Node = Node{ .id = .PtrType },
1906 op_token: TokenIndex,
1907 rhs: *Node,
1908 /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
1909 /// one of these possibly-null things. Then we have them directly follow the PtrType in memory.
1910 ptr_info: PtrInfo = .{},
1911
1912 pub fn iterate(self: *const PtrType, index: usize) ?*Node {
1913 var i = index;
1914
1915 if (self.ptr_info.sentinel) |sentinel| {
1916 if (i < 1) return sentinel;
1917 i -= 1;
1918 }
1919
1920 if (self.ptr_info.align_info) |align_info| {
1921 if (i < 1) return align_info.node;
1922 i -= 1;
18791923 }
18801924
18811925 if (i < 1) return self.rhs;
......@@ -1884,11 +1928,47 @@ pub const Node = struct {
18841928 return null;
18851929 }
18861930
1887 pub fn firstToken(self: *const PrefixOp) TokenIndex {
1931 pub fn firstToken(self: *const PtrType) TokenIndex {
18881932 return self.op_token;
18891933 }
18901934
1891 pub fn lastToken(self: *const PrefixOp) TokenIndex {
1935 pub fn lastToken(self: *const PtrType) TokenIndex {
1936 return self.rhs.lastToken();
1937 }
1938 };
1939
1940 pub const SliceType = struct {
1941 base: Node = Node{ .id = .SliceType },
1942 op_token: TokenIndex,
1943 rhs: *Node,
1944 /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
1945 /// one of these possibly-null things. Then we have them directly follow the SliceType in memory.
1946 ptr_info: PtrInfo = .{},
1947
1948 pub fn iterate(self: *const SliceType, index: usize) ?*Node {
1949 var i = index;
1950
1951 if (self.ptr_info.sentinel) |sentinel| {
1952 if (i < 1) return sentinel;
1953 i -= 1;
1954 }
1955
1956 if (self.ptr_info.align_info) |align_info| {
1957 if (i < 1) return align_info.node;
1958 i -= 1;
1959 }
1960
1961 if (i < 1) return self.rhs;
1962 i -= 1;
1963
1964 return null;
1965 }
1966
1967 pub fn firstToken(self: *const SliceType) TokenIndex {
1968 return self.op_token;
1969 }
1970
1971 pub fn lastToken(self: *const SliceType) TokenIndex {
18921972 return self.rhs.lastToken();
18931973 }
18941974 };
......@@ -2797,6 +2877,24 @@ pub const Node = struct {
27972877 };
27982878};
27992879
2880pub const PtrInfo = struct {
2881 allowzero_token: ?TokenIndex = null,
2882 align_info: ?Align = null,
2883 const_token: ?TokenIndex = null,
2884 volatile_token: ?TokenIndex = null,
2885 sentinel: ?*Node = null,
2886
2887 pub const Align = struct {
2888 node: *Node,
2889 bit_range: ?BitRange = null,
2890
2891 pub const BitRange = struct {
2892 start: *Node,
2893 end: *Node,
2894 };
2895 };
2896};
2897
28002898test "iterate" {
28012899 var root = Node.Root{
28022900 .base = Node{ .id = Node.Id.Root },
lib/std/zig/parse.zig+243-105
......@@ -1120,10 +1120,9 @@ const Parser = struct {
11201120 const expr_node = try p.expectNode(parseExpr, .{
11211121 .ExpectedExpr = .{ .token = p.tok_i },
11221122 });
1123 const node = try p.arena.allocator.create(Node.PrefixOp);
1123 const node = try p.arena.allocator.create(Node.Resume);
11241124 node.* = .{
11251125 .op_token = token,
1126 .op = .Resume,
11271126 .rhs = expr_node,
11281127 };
11291128 return &node.base;
......@@ -2413,24 +2412,25 @@ const Parser = struct {
24132412 /// / KEYWORD_await
24142413 fn parsePrefixOp(p: *Parser) !?*Node {
24152414 const token = p.nextToken();
2416 const op: Node.PrefixOp.Op = switch (p.token_ids[token]) {
2417 .Bang => .BoolNot,
2418 .Minus => .Negation,
2419 .Tilde => .BitNot,
2420 .MinusPercent => .NegationWrap,
2421 .Ampersand => .AddressOf,
2422 .Keyword_try => .Try,
2423 .Keyword_await => .Await,
2415 switch (p.token_ids[token]) {
2416 .Bang => return p.allocSimplePrefixOp(.BoolNot, token),
2417 .Minus => return p.allocSimplePrefixOp(.Negation, token),
2418 .Tilde => return p.allocSimplePrefixOp(.BitNot, token),
2419 .MinusPercent => return p.allocSimplePrefixOp(.NegationWrap, token),
2420 .Ampersand => return p.allocSimplePrefixOp(.AddressOf, token),
2421 .Keyword_try => return p.allocSimplePrefixOp(.Try, token),
2422 .Keyword_await => return p.allocSimplePrefixOp(.Await, token),
24242423 else => {
24252424 p.putBackToken(token);
24262425 return null;
24272426 },
2428 };
2427 }
2428 }
24292429
2430 const node = try p.arena.allocator.create(Node.PrefixOp);
2430 fn allocSimplePrefixOp(p: *Parser, comptime tag: Node.Id, token: TokenIndex) !?*Node {
2431 const node = try p.arena.allocator.create(Node.SimplePrefixOp(tag));
24312432 node.* = .{
24322433 .op_token = token,
2433 .op = op,
24342434 .rhs = undefined, // set by caller
24352435 };
24362436 return &node.base;
......@@ -2450,19 +2450,14 @@ const Parser = struct {
24502450 /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
24512451 fn parsePrefixTypeOp(p: *Parser) !?*Node {
24522452 if (p.eatToken(.QuestionMark)) |token| {
2453 const node = try p.arena.allocator.create(Node.PrefixOp);
2453 const node = try p.arena.allocator.create(Node.OptionalType);
24542454 node.* = .{
24552455 .op_token = token,
2456 .op = .OptionalType,
24572456 .rhs = undefined, // set by caller
24582457 };
24592458 return &node.base;
24602459 }
24612460
2462 // TODO: Returning a AnyFrameType instead of PrefixOp makes casting and setting .rhs or
2463 // .return_type more difficult for the caller (see parsePrefixOpExpr helper).
2464 // Consider making the AnyFrameType a member of PrefixOp and add a
2465 // PrefixOp.AnyFrameType variant?
24662461 if (p.eatToken(.Keyword_anyframe)) |token| {
24672462 const arrow = p.eatToken(.Arrow) orelse {
24682463 p.putBackToken(token);
......@@ -2482,11 +2477,15 @@ const Parser = struct {
24822477 if (try p.parsePtrTypeStart()) |node| {
24832478 // If the token encountered was **, there will be two nodes instead of one.
24842479 // The attributes should be applied to the rightmost operator.
2485 const prefix_op = node.cast(Node.PrefixOp).?;
2486 var ptr_info = if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk)
2487 &prefix_op.rhs.cast(Node.PrefixOp).?.op.PtrType
2480 var ptr_info = if (node.cast(Node.PtrType)) |ptr_type|
2481 if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk)
2482 &ptr_type.rhs.cast(Node.PtrType).?.ptr_info
2483 else
2484 &ptr_type.ptr_info
2485 else if (node.cast(Node.SliceType)) |slice_type|
2486 &slice_type.ptr_info
24882487 else
2489 &prefix_op.op.PtrType;
2488 unreachable;
24902489
24912490 while (true) {
24922491 if (p.eatToken(.Keyword_align)) |align_token| {
......@@ -2505,7 +2504,7 @@ const Parser = struct {
25052504 .ExpectedIntegerLiteral = .{ .token = p.tok_i },
25062505 });
25072506
2508 break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{
2507 break :bit_range_value ast.PtrInfo.Align.BitRange{
25092508 .start = range_start,
25102509 .end = range_end,
25112510 };
......@@ -2519,7 +2518,7 @@ const Parser = struct {
25192518 continue;
25202519 }
25212520
2522 ptr_info.align_info = Node.PrefixOp.PtrInfo.Align{
2521 ptr_info.align_info = ast.PtrInfo.Align{
25232522 .node = expr_node,
25242523 .bit_range = bit_range,
25252524 };
......@@ -2563,58 +2562,54 @@ const Parser = struct {
25632562 }
25642563
25652564 if (try p.parseArrayTypeStart()) |node| {
2566 switch (node.cast(Node.PrefixOp).?.op) {
2567 .ArrayType => {},
2568 .SliceType => |*slice_type| {
2569 // Collect pointer qualifiers in any order, but disallow duplicates
2570 while (true) {
2571 if (try p.parseByteAlign()) |align_expr| {
2572 if (slice_type.align_info != null) {
2573 try p.errors.append(p.gpa, .{
2574 .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
2575 });
2576 continue;
2577 }
2578 slice_type.align_info = Node.PrefixOp.PtrInfo.Align{
2579 .node = align_expr,
2580 .bit_range = null,
2581 };
2565 if (node.cast(Node.SliceType)) |slice_type| {
2566 // Collect pointer qualifiers in any order, but disallow duplicates
2567 while (true) {
2568 if (try p.parseByteAlign()) |align_expr| {
2569 if (slice_type.ptr_info.align_info != null) {
2570 try p.errors.append(p.gpa, .{
2571 .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
2572 });
25822573 continue;
25832574 }
2584 if (p.eatToken(.Keyword_const)) |const_token| {
2585 if (slice_type.const_token != null) {
2586 try p.errors.append(p.gpa, .{
2587 .ExtraConstQualifier = .{ .token = p.tok_i - 1 },
2588 });
2589 continue;
2590 }
2591 slice_type.const_token = const_token;
2575 slice_type.ptr_info.align_info = ast.PtrInfo.Align{
2576 .node = align_expr,
2577 .bit_range = null,
2578 };
2579 continue;
2580 }
2581 if (p.eatToken(.Keyword_const)) |const_token| {
2582 if (slice_type.ptr_info.const_token != null) {
2583 try p.errors.append(p.gpa, .{
2584 .ExtraConstQualifier = .{ .token = p.tok_i - 1 },
2585 });
25922586 continue;
25932587 }
2594 if (p.eatToken(.Keyword_volatile)) |volatile_token| {
2595 if (slice_type.volatile_token != null) {
2596 try p.errors.append(p.gpa, .{
2597 .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
2598 });
2599 continue;
2600 }
2601 slice_type.volatile_token = volatile_token;
2588 slice_type.ptr_info.const_token = const_token;
2589 continue;
2590 }
2591 if (p.eatToken(.Keyword_volatile)) |volatile_token| {
2592 if (slice_type.ptr_info.volatile_token != null) {
2593 try p.errors.append(p.gpa, .{
2594 .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
2595 });
26022596 continue;
26032597 }
2604 if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
2605 if (slice_type.allowzero_token != null) {
2606 try p.errors.append(p.gpa, .{
2607 .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
2608 });
2609 continue;
2610 }
2611 slice_type.allowzero_token = allowzero_token;
2598 slice_type.ptr_info.volatile_token = volatile_token;
2599 continue;
2600 }
2601 if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
2602 if (slice_type.ptr_info.allowzero_token != null) {
2603 try p.errors.append(p.gpa, .{
2604 .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
2605 });
26122606 continue;
26132607 }
2614 break;
2608 slice_type.ptr_info.allowzero_token = allowzero_token;
2609 continue;
26152610 }
2616 },
2617 else => unreachable,
2611 break;
2612 }
26182613 }
26192614 return node;
26202615 }
......@@ -2728,29 +2723,32 @@ const Parser = struct {
27282723 null;
27292724 const rbracket = try p.expectToken(.RBracket);
27302725
2731 const op: Node.PrefixOp.Op = if (expr) |len_expr|
2732 .{
2733 .ArrayType = .{
2726 if (expr) |len_expr| {
2727 if (sentinel) |s| {
2728 const node = try p.arena.allocator.create(Node.ArrayTypeSentinel);
2729 node.* = .{
2730 .op_token = lbracket,
2731 .rhs = undefined, // set by caller
27342732 .len_expr = len_expr,
2735 .sentinel = sentinel,
2736 },
2733 .sentinel = s,
2734 };
2735 return &node.base;
2736 } else {
2737 const node = try p.arena.allocator.create(Node.ArrayType);
2738 node.* = .{
2739 .op_token = lbracket,
2740 .rhs = undefined, // set by caller
2741 .len_expr = len_expr,
2742 };
2743 return &node.base;
27372744 }
2738 else
2739 .{
2740 .SliceType = Node.PrefixOp.PtrInfo{
2741 .allowzero_token = null,
2742 .align_info = null,
2743 .const_token = null,
2744 .volatile_token = null,
2745 .sentinel = sentinel,
2746 },
2747 };
2745 }
27482746
2749 const node = try p.arena.allocator.create(Node.PrefixOp);
2747 const node = try p.arena.allocator.create(Node.SliceType);
27502748 node.* = .{
27512749 .op_token = lbracket,
2752 .op = op,
27532750 .rhs = undefined, // set by caller
2751 .ptr_info = .{ .sentinel = sentinel },
27542752 };
27552753 return &node.base;
27562754 }
......@@ -2768,28 +2766,26 @@ const Parser = struct {
27682766 })
27692767 else
27702768 null;
2771 const node = try p.arena.allocator.create(Node.PrefixOp);
2769 const node = try p.arena.allocator.create(Node.PtrType);
27722770 node.* = .{
27732771 .op_token = asterisk,
2774 .op = .{ .PtrType = .{ .sentinel = sentinel } },
27752772 .rhs = undefined, // set by caller
2773 .ptr_info = .{ .sentinel = sentinel },
27762774 };
27772775 return &node.base;
27782776 }
27792777
27802778 if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| {
2781 const node = try p.arena.allocator.create(Node.PrefixOp);
2779 const node = try p.arena.allocator.create(Node.PtrType);
27822780 node.* = .{
27832781 .op_token = double_asterisk,
2784 .op = .{ .PtrType = .{} },
27852782 .rhs = undefined, // set by caller
27862783 };
27872784
27882785 // Special case for **, which is its own token
2789 const child = try p.arena.allocator.create(Node.PrefixOp);
2786 const child = try p.arena.allocator.create(Node.PtrType);
27902787 child.* = .{
27912788 .op_token = double_asterisk,
2792 .op = .{ .PtrType = .{} },
27932789 .rhs = undefined, // set by caller
27942790 };
27952791 node.rhs = &child.base;
......@@ -2808,10 +2804,9 @@ const Parser = struct {
28082804 p.putBackToken(ident);
28092805 } else {
28102806 _ = try p.expectToken(.RBracket);
2811 const node = try p.arena.allocator.create(Node.PrefixOp);
2807 const node = try p.arena.allocator.create(Node.PtrType);
28122808 node.* = .{
28132809 .op_token = lbracket,
2814 .op = .{ .PtrType = .{} },
28152810 .rhs = undefined, // set by caller
28162811 };
28172812 return &node.base;
......@@ -2824,11 +2819,11 @@ const Parser = struct {
28242819 else
28252820 null;
28262821 _ = try p.expectToken(.RBracket);
2827 const node = try p.arena.allocator.create(Node.PrefixOp);
2822 const node = try p.arena.allocator.create(Node.PtrType);
28282823 node.* = .{
28292824 .op_token = lbracket,
2830 .op = .{ .PtrType = .{ .sentinel = sentinel } },
28312825 .rhs = undefined, // set by caller
2826 .ptr_info = .{ .sentinel = sentinel },
28322827 };
28332828 return &node.base;
28342829 }
......@@ -3146,10 +3141,9 @@ const Parser = struct {
31463141
31473142 fn parseTry(p: *Parser) !?*Node {
31483143 const token = p.eatToken(.Keyword_try) orelse return null;
3149 const node = try p.arena.allocator.create(Node.PrefixOp);
3144 const node = try p.arena.allocator.create(Node.Try);
31503145 node.* = .{
31513146 .op_token = token,
3152 .op = .Try,
31533147 .rhs = undefined, // set by caller
31543148 };
31553149 return &node.base;
......@@ -3228,15 +3222,87 @@ const Parser = struct {
32283222 var rightmost_op = first_op;
32293223 while (true) {
32303224 switch (rightmost_op.id) {
3231 .PrefixOp => {
3232 var prefix_op = rightmost_op.cast(Node.PrefixOp).?;
3225 .AddressOf => {
3226 if (try opParseFn(p)) |rhs| {
3227 rightmost_op.cast(Node.AddressOf).?.rhs = rhs;
3228 rightmost_op = rhs;
3229 } else break;
3230 },
3231 .Await => {
3232 if (try opParseFn(p)) |rhs| {
3233 rightmost_op.cast(Node.Await).?.rhs = rhs;
3234 rightmost_op = rhs;
3235 } else break;
3236 },
3237 .BitNot => {
3238 if (try opParseFn(p)) |rhs| {
3239 rightmost_op.cast(Node.BitNot).?.rhs = rhs;
3240 rightmost_op = rhs;
3241 } else break;
3242 },
3243 .BoolNot => {
3244 if (try opParseFn(p)) |rhs| {
3245 rightmost_op.cast(Node.BoolNot).?.rhs = rhs;
3246 rightmost_op = rhs;
3247 } else break;
3248 },
3249 .OptionalType => {
3250 if (try opParseFn(p)) |rhs| {
3251 rightmost_op.cast(Node.OptionalType).?.rhs = rhs;
3252 rightmost_op = rhs;
3253 } else break;
3254 },
3255 .Negation => {
3256 if (try opParseFn(p)) |rhs| {
3257 rightmost_op.cast(Node.Negation).?.rhs = rhs;
3258 rightmost_op = rhs;
3259 } else break;
3260 },
3261 .NegationWrap => {
3262 if (try opParseFn(p)) |rhs| {
3263 rightmost_op.cast(Node.NegationWrap).?.rhs = rhs;
3264 rightmost_op = rhs;
3265 } else break;
3266 },
3267 .Resume => {
3268 if (try opParseFn(p)) |rhs| {
3269 rightmost_op.cast(Node.Resume).?.rhs = rhs;
3270 rightmost_op = rhs;
3271 } else break;
3272 },
3273 .Try => {
3274 if (try opParseFn(p)) |rhs| {
3275 rightmost_op.cast(Node.Try).?.rhs = rhs;
3276 rightmost_op = rhs;
3277 } else break;
3278 },
3279 .ArrayType => {
3280 if (try opParseFn(p)) |rhs| {
3281 rightmost_op.cast(Node.ArrayType).?.rhs = rhs;
3282 rightmost_op = rhs;
3283 } else break;
3284 },
3285 .ArrayTypeSentinel => {
3286 if (try opParseFn(p)) |rhs| {
3287 rightmost_op.cast(Node.ArrayTypeSentinel).?.rhs = rhs;
3288 rightmost_op = rhs;
3289 } else break;
3290 },
3291 .SliceType => {
3292 if (try opParseFn(p)) |rhs| {
3293 rightmost_op.cast(Node.SliceType).?.rhs = rhs;
3294 rightmost_op = rhs;
3295 } else break;
3296 },
3297 .PtrType => {
3298 var ptr_type = rightmost_op.cast(Node.PtrType).?;
32333299 // If the token encountered was **, there will be two nodes
3234 if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk) {
3235 rightmost_op = prefix_op.rhs;
3236 prefix_op = rightmost_op.cast(Node.PrefixOp).?;
3300 if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) {
3301 rightmost_op = ptr_type.rhs;
3302 ptr_type = rightmost_op.cast(Node.PtrType).?;
32373303 }
32383304 if (try opParseFn(p)) |rhs| {
3239 prefix_op.rhs = rhs;
3305 ptr_type.rhs = rhs;
32403306 rightmost_op = rhs;
32413307 } else break;
32423308 },
......@@ -3253,8 +3319,80 @@ const Parser = struct {
32533319
32543320 // If any prefix op existed, a child node on the RHS is required
32553321 switch (rightmost_op.id) {
3256 .PrefixOp => {
3257 const prefix_op = rightmost_op.cast(Node.PrefixOp).?;
3322 .AddressOf => {
3323 const prefix_op = rightmost_op.cast(Node.AddressOf).?;
3324 prefix_op.rhs = try p.expectNode(childParseFn, .{
3325 .InvalidToken = .{ .token = p.tok_i },
3326 });
3327 },
3328 .Await => {
3329 const prefix_op = rightmost_op.cast(Node.Await).?;
3330 prefix_op.rhs = try p.expectNode(childParseFn, .{
3331 .InvalidToken = .{ .token = p.tok_i },
3332 });
3333 },
3334 .BitNot => {
3335 const prefix_op = rightmost_op.cast(Node.BitNot).?;
3336 prefix_op.rhs = try p.expectNode(childParseFn, .{
3337 .InvalidToken = .{ .token = p.tok_i },
3338 });
3339 },
3340 .BoolNot => {
3341 const prefix_op = rightmost_op.cast(Node.BoolNot).?;
3342 prefix_op.rhs = try p.expectNode(childParseFn, .{
3343 .InvalidToken = .{ .token = p.tok_i },
3344 });
3345 },
3346 .OptionalType => {
3347 const prefix_op = rightmost_op.cast(Node.OptionalType).?;
3348 prefix_op.rhs = try p.expectNode(childParseFn, .{
3349 .InvalidToken = .{ .token = p.tok_i },
3350 });
3351 },
3352 .Negation => {
3353 const prefix_op = rightmost_op.cast(Node.Negation).?;
3354 prefix_op.rhs = try p.expectNode(childParseFn, .{
3355 .InvalidToken = .{ .token = p.tok_i },
3356 });
3357 },
3358 .NegationWrap => {
3359 const prefix_op = rightmost_op.cast(Node.NegationWrap).?;
3360 prefix_op.rhs = try p.expectNode(childParseFn, .{
3361 .InvalidToken = .{ .token = p.tok_i },
3362 });
3363 },
3364 .Resume => {
3365 const prefix_op = rightmost_op.cast(Node.Resume).?;
3366 prefix_op.rhs = try p.expectNode(childParseFn, .{
3367 .InvalidToken = .{ .token = p.tok_i },
3368 });
3369 },
3370 .Try => {
3371 const prefix_op = rightmost_op.cast(Node.Try).?;
3372 prefix_op.rhs = try p.expectNode(childParseFn, .{
3373 .InvalidToken = .{ .token = p.tok_i },
3374 });
3375 },
3376 .ArrayType => {
3377 const prefix_op = rightmost_op.cast(Node.ArrayType).?;
3378 prefix_op.rhs = try p.expectNode(childParseFn, .{
3379 .InvalidToken = .{ .token = p.tok_i },
3380 });
3381 },
3382 .ArrayTypeSentinel => {
3383 const prefix_op = rightmost_op.cast(Node.ArrayTypeSentinel).?;
3384 prefix_op.rhs = try p.expectNode(childParseFn, .{
3385 .InvalidToken = .{ .token = p.tok_i },
3386 });
3387 },
3388 .PtrType => {
3389 const prefix_op = rightmost_op.cast(Node.PtrType).?;
3390 prefix_op.rhs = try p.expectNode(childParseFn, .{
3391 .InvalidToken = .{ .token = p.tok_i },
3392 });
3393 },
3394 .SliceType => {
3395 const prefix_op = rightmost_op.cast(Node.SliceType).?;
32583396 prefix_op.rhs = try p.expectNode(childParseFn, .{
32593397 .InvalidToken = .{ .token = p.tok_i },
32603398 });
lib/std/zig/render.zig+211-145
......@@ -468,166 +468,192 @@ fn renderExpression(
468468 return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);
469469 },
470470
471 .PrefixOp => {
472 const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);
473
474 switch (prefix_op_node.op) {
475 .PtrType => |ptr_info| {
476 const op_tok_id = tree.token_ids[prefix_op_node.op_token];
477 switch (op_tok_id) {
478 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
479 .LBracket => if (tree.token_ids[prefix_op_node.op_token + 2] == .Identifier)
480 try stream.writeAll("[*c")
481 else
482 try stream.writeAll("[*"),
483 else => unreachable,
484 }
485 if (ptr_info.sentinel) |sentinel| {
486 const colon_token = tree.prevToken(sentinel.firstToken());
487 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
488 const sentinel_space = switch (op_tok_id) {
489 .LBracket => Space.None,
490 else => Space.Space,
491 };
492 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space);
493 }
494 switch (op_tok_id) {
495 .Asterisk, .AsteriskAsterisk => {},
496 .LBracket => try stream.writeByte(']'),
497 else => unreachable,
498 }
499 if (ptr_info.allowzero_token) |allowzero_token| {
500 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
501 }
502 if (ptr_info.align_info) |align_info| {
503 const lparen_token = tree.prevToken(align_info.node.firstToken());
504 const align_token = tree.prevToken(lparen_token);
505
506 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
507 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
508
509 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
510
511 if (align_info.bit_range) |bit_range| {
512 const colon1 = tree.prevToken(bit_range.start.firstToken());
513 const colon2 = tree.prevToken(bit_range.end.firstToken());
471 .BitNot => {
472 const bit_not = @fieldParentPtr(ast.Node.BitNot, "base", base);
473 try renderToken(tree, stream, bit_not.op_token, indent, start_col, Space.None);
474 return renderExpression(allocator, stream, tree, indent, start_col, bit_not.rhs, space);
475 },
476 .BoolNot => {
477 const bool_not = @fieldParentPtr(ast.Node.BoolNot, "base", base);
478 try renderToken(tree, stream, bool_not.op_token, indent, start_col, Space.None);
479 return renderExpression(allocator, stream, tree, indent, start_col, bool_not.rhs, space);
480 },
481 .Negation => {
482 const negation = @fieldParentPtr(ast.Node.Negation, "base", base);
483 try renderToken(tree, stream, negation.op_token, indent, start_col, Space.None);
484 return renderExpression(allocator, stream, tree, indent, start_col, negation.rhs, space);
485 },
486 .NegationWrap => {
487 const negation_wrap = @fieldParentPtr(ast.Node.NegationWrap, "base", base);
488 try renderToken(tree, stream, negation_wrap.op_token, indent, start_col, Space.None);
489 return renderExpression(allocator, stream, tree, indent, start_col, negation_wrap.rhs, space);
490 },
491 .OptionalType => {
492 const opt_type = @fieldParentPtr(ast.Node.OptionalType, "base", base);
493 try renderToken(tree, stream, opt_type.op_token, indent, start_col, Space.None);
494 return renderExpression(allocator, stream, tree, indent, start_col, opt_type.rhs, space);
495 },
496 .AddressOf => {
497 const addr_of = @fieldParentPtr(ast.Node.AddressOf, "base", base);
498 try renderToken(tree, stream, addr_of.op_token, indent, start_col, Space.None);
499 return renderExpression(allocator, stream, tree, indent, start_col, addr_of.rhs, space);
500 },
501 .Try => {
502 const try_node = @fieldParentPtr(ast.Node.Try, "base", base);
503 try renderToken(tree, stream, try_node.op_token, indent, start_col, Space.Space);
504 return renderExpression(allocator, stream, tree, indent, start_col, try_node.rhs, space);
505 },
506 .Resume => {
507 const resume_node = @fieldParentPtr(ast.Node.Resume, "base", base);
508 try renderToken(tree, stream, resume_node.op_token, indent, start_col, Space.Space);
509 return renderExpression(allocator, stream, tree, indent, start_col, resume_node.rhs, space);
510 },
511 .Await => {
512 const await_node = @fieldParentPtr(ast.Node.Await, "base", base);
513 try renderToken(tree, stream, await_node.op_token, indent, start_col, Space.Space);
514 return renderExpression(allocator, stream, tree, indent, start_col, await_node.rhs, space);
515 },
514516
515 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
516 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
517 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
518 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
517 .ArrayType => {
518 const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base);
519 return renderArrayType(
520 allocator,
521 stream,
522 tree,
523 indent,
524 start_col,
525 array_type.op_token,
526 array_type.rhs,
527 array_type.len_expr,
528 null,
529 space,
530 );
531 },
532 .ArrayTypeSentinel => {
533 const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base);
534 return renderArrayType(
535 allocator,
536 stream,
537 tree,
538 indent,
539 start_col,
540 array_type.op_token,
541 array_type.rhs,
542 array_type.len_expr,
543 array_type.sentinel,
544 space,
545 );
546 },
519547
520 const rparen_token = tree.nextToken(bit_range.end.lastToken());
521 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
522 } else {
523 const rparen_token = tree.nextToken(align_info.node.lastToken());
524 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
525 }
526 }
527 if (ptr_info.const_token) |const_token| {
528 try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const
529 }
530 if (ptr_info.volatile_token) |volatile_token| {
531 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile
532 }
533 },
548 .PtrType => {
549 const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base);
550 const op_tok_id = tree.token_ids[ptr_type.op_token];
551 switch (op_tok_id) {
552 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
553 .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier)
554 try stream.writeAll("[*c")
555 else
556 try stream.writeAll("[*"),
557 else => unreachable,
558 }
559 if (ptr_type.ptr_info.sentinel) |sentinel| {
560 const colon_token = tree.prevToken(sentinel.firstToken());
561 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
562 const sentinel_space = switch (op_tok_id) {
563 .LBracket => Space.None,
564 else => Space.Space,
565 };
566 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space);
567 }
568 switch (op_tok_id) {
569 .Asterisk, .AsteriskAsterisk => {},
570 .LBracket => try stream.writeByte(']'),
571 else => unreachable,
572 }
573 if (ptr_type.ptr_info.allowzero_token) |allowzero_token| {
574 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
575 }
576 if (ptr_type.ptr_info.align_info) |align_info| {
577 const lparen_token = tree.prevToken(align_info.node.firstToken());
578 const align_token = tree.prevToken(lparen_token);
534579
535 .SliceType => |ptr_info| {
536 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [
537 if (ptr_info.sentinel) |sentinel| {
538 const colon_token = tree.prevToken(sentinel.firstToken());
539 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
540 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
541 try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ]
542 } else {
543 try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ]
544 }
580 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
581 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
545582
546 if (ptr_info.allowzero_token) |allowzero_token| {
547 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
548 }
549 if (ptr_info.align_info) |align_info| {
550 const lparen_token = tree.prevToken(align_info.node.firstToken());
551 const align_token = tree.prevToken(lparen_token);
583 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
552584
553 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
554 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
585 if (align_info.bit_range) |bit_range| {
586 const colon1 = tree.prevToken(bit_range.start.firstToken());
587 const colon2 = tree.prevToken(bit_range.end.firstToken());
555588
556 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
589 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
590 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
591 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
592 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
557593
558 if (align_info.bit_range) |bit_range| {
559 const colon1 = tree.prevToken(bit_range.start.firstToken());
560 const colon2 = tree.prevToken(bit_range.end.firstToken());
594 const rparen_token = tree.nextToken(bit_range.end.lastToken());
595 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
596 } else {
597 const rparen_token = tree.nextToken(align_info.node.lastToken());
598 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
599 }
600 }
601 if (ptr_type.ptr_info.const_token) |const_token| {
602 try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const
603 }
604 if (ptr_type.ptr_info.volatile_token) |volatile_token| {
605 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile
606 }
607 return renderExpression(allocator, stream, tree, indent, start_col, ptr_type.rhs, space);
608 },
561609
562 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
563 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
564 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
565 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
610 .SliceType => {
611 const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base);
612 try renderToken(tree, stream, slice_type.op_token, indent, start_col, Space.None); // [
613 if (slice_type.ptr_info.sentinel) |sentinel| {
614 const colon_token = tree.prevToken(sentinel.firstToken());
615 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
616 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
617 try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ]
618 } else {
619 try renderToken(tree, stream, tree.nextToken(slice_type.op_token), indent, start_col, Space.None); // ]
620 }
566621
567 const rparen_token = tree.nextToken(bit_range.end.lastToken());
568 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
569 } else {
570 const rparen_token = tree.nextToken(align_info.node.lastToken());
571 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
572 }
573 }
574 if (ptr_info.const_token) |const_token| {
575 try renderToken(tree, stream, const_token, indent, start_col, Space.Space);
576 }
577 if (ptr_info.volatile_token) |volatile_token| {
578 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space);
579 }
580 },
622 if (slice_type.ptr_info.allowzero_token) |allowzero_token| {
623 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
624 }
625 if (slice_type.ptr_info.align_info) |align_info| {
626 const lparen_token = tree.prevToken(align_info.node.firstToken());
627 const align_token = tree.prevToken(lparen_token);
581628
582 .ArrayType => |array_info| {
583 const lbracket = prefix_op_node.op_token;
584 const rbracket = tree.nextToken(if (array_info.sentinel) |sentinel|
585 sentinel.lastToken()
586 else
587 array_info.len_expr.lastToken());
629 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
630 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
588631
589 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
632 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
590633
591 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
592 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
593 const new_indent = if (ends_with_comment) indent + indent_delta else indent;
594 const new_space = if (ends_with_comment) Space.Newline else Space.None;
595 try renderExpression(allocator, stream, tree, new_indent, start_col, array_info.len_expr, new_space);
596 if (starts_with_comment) {
597 try stream.writeByte('\n');
598 }
599 if (ends_with_comment or starts_with_comment) {
600 try stream.writeByteNTimes(' ', indent);
601 }
602 if (array_info.sentinel) |sentinel| {
603 const colon_token = tree.prevToken(sentinel.firstToken());
604 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
605 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
606 }
607 try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ]
608 },
609 .BitNot,
610 .BoolNot,
611 .Negation,
612 .NegationWrap,
613 .OptionalType,
614 .AddressOf,
615 => {
616 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None);
617 },
634 if (align_info.bit_range) |bit_range| {
635 const colon1 = tree.prevToken(bit_range.start.firstToken());
636 const colon2 = tree.prevToken(bit_range.end.firstToken());
618637
619 .Try,
620 .Resume,
621 => {
622 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);
623 },
638 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
639 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
640 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
641 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
624642
625 .Await => |await_info| {
626 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);
627 },
643 const rparen_token = tree.nextToken(bit_range.end.lastToken());
644 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
645 } else {
646 const rparen_token = tree.nextToken(align_info.node.lastToken());
647 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
648 }
628649 }
629
630 return renderExpression(allocator, stream, tree, indent, start_col, prefix_op_node.rhs, space);
650 if (slice_type.ptr_info.const_token) |const_token| {
651 try renderToken(tree, stream, const_token, indent, start_col, Space.Space);
652 }
653 if (slice_type.ptr_info.volatile_token) |volatile_token| {
654 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space);
655 }
656 return renderExpression(allocator, stream, tree, indent, start_col, slice_type.rhs, space);
631657 },
632658
633659 .ArrayInitializer, .ArrayInitializerDot => {
......@@ -2057,6 +2083,46 @@ fn renderExpression(
20572083 }
20582084}
20592085
2086fn renderArrayType(
2087 allocator: *mem.Allocator,
2088 stream: anytype,
2089 tree: *ast.Tree,
2090 indent: usize,
2091 start_col: *usize,
2092 lbracket: ast.TokenIndex,
2093 rhs: *ast.Node,
2094 len_expr: *ast.Node,
2095 opt_sentinel: ?*ast.Node,
2096 space: Space,
2097) (@TypeOf(stream).Error || Error)!void {
2098 const rbracket = tree.nextToken(if (opt_sentinel) |sentinel|
2099 sentinel.lastToken()
2100 else
2101 len_expr.lastToken());
2102
2103 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
2104
2105 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
2106 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
2107 const new_indent = if (ends_with_comment) indent + indent_delta else indent;
2108 const new_space = if (ends_with_comment) Space.Newline else Space.None;
2109 try renderExpression(allocator, stream, tree, new_indent, start_col, len_expr, new_space);
2110 if (starts_with_comment) {
2111 try stream.writeByte('\n');
2112 }
2113 if (ends_with_comment or starts_with_comment) {
2114 try stream.writeByteNTimes(' ', indent);
2115 }
2116 if (opt_sentinel) |sentinel| {
2117 const colon_token = tree.prevToken(sentinel.firstToken());
2118 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
2119 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
2120 }
2121 try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ]
2122
2123 return renderExpression(allocator, stream, tree, indent, start_col, rhs, space);
2124}
2125
20602126fn renderAsmOutput(
20612127 allocator: *mem.Allocator,
20622128 stream: anytype,
src-self-hosted/Module.zig+48-23
......@@ -36,7 +36,7 @@ bin_file_path: []const u8,
3636decl_exports: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
3737/// We track which export is associated with the given symbol name for quick
3838/// detection of symbol collisions.
39symbol_exports: std.StringHashMap(*Export),
39symbol_exports: std.StringHashMapUnmanaged(*Export) = .{},
4040/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
4141/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
4242/// is performing the export of another Decl.
......@@ -769,7 +769,6 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
769769 .bin_file_path = options.bin_file_path,
770770 .bin_file = bin_file,
771771 .optimize_mode = options.optimize_mode,
772 .symbol_exports = std.StringHashMap(*Export).init(gpa),
773772 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
774773 .keep_source_files_loaded = options.keep_source_files_loaded,
775774 };
......@@ -812,7 +811,7 @@ pub fn deinit(self: *Module) void {
812811 }
813812 self.export_owners.deinit(gpa);
814813
815 self.symbol_exports.deinit();
814 self.symbol_exports.deinit(gpa);
816815 self.root_scope.destroy(gpa);
817816 self.* = undefined;
818817}
......@@ -1309,10 +1308,18 @@ fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir
13091308 .ControlFlowExpression => return self.astGenControlFlowExpression(scope, @fieldParentPtr(ast.Node.ControlFlowExpression, "base", ast_node)),
13101309 .If => return self.astGenIf(scope, @fieldParentPtr(ast.Node.If, "base", ast_node)),
13111310 .InfixOp => return self.astGenInfixOp(scope, @fieldParentPtr(ast.Node.InfixOp, "base", ast_node)),
1311 .BoolNot => return self.astGenBoolNot(scope, @fieldParentPtr(ast.Node.BoolNot, "base", ast_node)),
13121312 else => return self.failNode(scope, ast_node, "TODO implement astGenExpr for {}", .{@tagName(ast_node.id)}),
13131313 }
13141314}
13151315
1316fn astGenBoolNot(self: *Module, scope: *Scope, node: *ast.Node.BoolNot) InnerError!*zir.Inst {
1317 const operand = try self.astGenExpr(scope, node.rhs);
1318 const tree = scope.tree();
1319 const src = tree.token_locs[node.op_token].start;
1320 return self.addZIRInst(scope, src, zir.Inst.BoolNot, .{ .operand = operand }, .{});
1321}
1322
13161323fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) InnerError!*zir.Inst {
13171324 switch (infix_node.op) {
13181325 .Assign => {
......@@ -1351,17 +1358,19 @@ fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) In
13511358 const tree = scope.tree();
13521359 const src = tree.token_locs[infix_node.op_token].start;
13531360
1361 const op: std.math.CompareOperator = switch (infix_node.op) {
1362 .BangEqual => .neq,
1363 .EqualEqual => .eq,
1364 .GreaterThan => .gt,
1365 .GreaterOrEqual => .gte,
1366 .LessThan => .lt,
1367 .LessOrEqual => .lte,
1368 else => unreachable,
1369 };
1370
13541371 return self.addZIRInst(scope, src, zir.Inst.Cmp, .{
13551372 .lhs = lhs,
1356 .op = @as(std.math.CompareOperator, switch (infix_node.op) {
1357 .BangEqual => .neq,
1358 .EqualEqual => .eq,
1359 .GreaterThan => .gt,
1360 .GreaterOrEqual => .gte,
1361 .LessThan => .lt,
1362 .LessOrEqual => .lte,
1363 else => unreachable,
1364 }),
1373 .op = op,
13651374 .rhs = rhs,
13661375 }, .{});
13671376 },
......@@ -1408,11 +1417,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir
14081417 defer then_scope.instructions.deinit(self.gpa);
14091418
14101419 const then_result = try self.astGenExpr(&then_scope.base, if_node.body);
1411 const then_src = tree.token_locs[if_node.body.lastToken()].start;
1412 _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
1413 .block = block,
1414 .operand = then_result,
1415 }, .{});
1420 if (!then_result.tag.isNoReturn()) {
1421 const then_src = tree.token_locs[if_node.body.lastToken()].start;
1422 _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
1423 .block = block,
1424 .operand = then_result,
1425 }, .{});
1426 }
14161427 condbr.positionals.true_body = .{
14171428 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
14181429 };
......@@ -1426,11 +1437,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir
14261437
14271438 if (if_node.@"else") |else_node| {
14281439 const else_result = try self.astGenExpr(&else_scope.base, else_node.body);
1429 const else_src = tree.token_locs[else_node.body.lastToken()].start;
1430 _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
1431 .block = block,
1432 .operand = else_result,
1433 }, .{});
1440 if (!else_result.tag.isNoReturn()) {
1441 const else_src = tree.token_locs[else_node.body.lastToken()].start;
1442 _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
1443 .block = block,
1444 .operand = else_result,
1445 }, .{});
1446 }
14341447 } else {
14351448 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
14361449 // by directly allocating the body for this one instruction.
......@@ -2305,7 +2318,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
23052318 return;
23062319 }
23072320
2308 try self.symbol_exports.putNoClobber(symbol_name, new_export);
2321 try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export);
23092322 self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
23102323 error.OutOfMemory => return error.OutOfMemory,
23112324 else => {
......@@ -2559,6 +2572,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
25592572 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?),
25602573 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?),
25612574 .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(zir.Inst.IsNonNull).?),
2575 .boolnot => return self.analyzeInstBoolNot(scope, old_inst.cast(zir.Inst.BoolNot).?),
25622576 }
25632577}
25642578
......@@ -3236,6 +3250,17 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!
32363250 return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
32373251}
32383252
3253fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.BoolNot) InnerError!*Inst {
3254 const uncasted_operand = try self.resolveInst(scope, inst.positionals.operand);
3255 const bool_type = Type.initTag(.bool);
3256 const operand = try self.coerce(scope, bool_type, uncasted_operand);
3257 if (try self.resolveDefinedValue(scope, operand)) |val| {
3258 return self.constBool(scope, inst.base.src, !val.toBool());
3259 }
3260 const b = try self.requireRuntimeBlock(scope, inst.base.src);
3261 return self.addNewInstArgs(b, inst.base.src, bool_type, Inst.Not, .{ .operand = operand });
3262}
3263
32393264fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {
32403265 const operand = try self.resolveInst(scope, inst.positionals.operand);
32413266 return self.analyzeIsNull(scope, inst.base.src, operand, true);
src-self-hosted/codegen.zig+82-4
......@@ -407,6 +407,55 @@ const Function = struct {
407407 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),
408408 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?, arch),
409409 .unreach => return MCValue{ .unreach = {} },
410 .not => return self.genNot(inst.cast(ir.Inst.Not).?, arch),
411 }
412 }
413
414 fn genNot(self: *Function, inst: *ir.Inst.Not, comptime arch: std.Target.Cpu.Arch) !MCValue {
415 // No side effects, so if it's unreferenced, do nothing.
416 if (inst.base.isUnused())
417 return MCValue.dead;
418 const operand = try self.resolveInst(inst.args.operand);
419 switch (operand) {
420 .dead => unreachable,
421 .unreach => unreachable,
422 .compare_flags_unsigned => |op| return MCValue{
423 .compare_flags_unsigned = switch (op) {
424 .gte => .lt,
425 .gt => .lte,
426 .neq => .eq,
427 .lt => .gte,
428 .lte => .gt,
429 .eq => .neq,
430 },
431 },
432 .compare_flags_signed => |op| return MCValue{
433 .compare_flags_signed = switch (op) {
434 .gte => .lt,
435 .gt => .lte,
436 .neq => .eq,
437 .lt => .gte,
438 .lte => .gt,
439 .eq => .neq,
440 },
441 },
442 else => {},
443 }
444
445 switch (arch) {
446 .x86_64 => {
447 var imm = ir.Inst.Constant{
448 .base = .{
449 .tag = .constant,
450 .deaths = 0,
451 .ty = inst.args.operand.ty,
452 .src = inst.args.operand.src,
453 },
454 .val = Value.initTag(.bool_true),
455 };
456 return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30);
457 },
458 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
410459 }
411460 }
412461
......@@ -434,7 +483,7 @@ const Function = struct {
434483 }
435484 }
436485
437 /// ADD, SUB
486 /// ADD, SUB, XOR, OR, AND
438487 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
439488 try self.code.ensureCapacity(self.code.items.len + 8);
440489
......@@ -695,7 +744,7 @@ const Function = struct {
695744
696745 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
697746 switch (arch) {
698 .i386, .x86_64 => {
747 .x86_64 => {
699748 try self.code.ensureCapacity(self.code.items.len + 6);
700749
701750 const cond = try self.resolveInst(inst.args.condition);
......@@ -724,7 +773,20 @@ const Function = struct {
724773 };
725774 return self.genX86CondBr(inst, opcode, arch);
726775 },
727 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition not already in the compare flags", .{self.target.cpu.arch}),
776 .register => |reg_usize| {
777 const reg = @intToEnum(Reg(arch), @intCast(u8, reg_usize));
778 // test reg, 1
779 // TODO detect al, ax, eax
780 try self.code.ensureCapacity(self.code.items.len + 4);
781 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
782 self.code.appendSliceAssumeCapacity(&[_]u8{
783 0xf6,
784 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
785 0x01,
786 });
787 return self.genX86CondBr(inst, 0x84, arch);
788 },
789 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
728790 }
729791 },
730792 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
......@@ -812,6 +874,8 @@ const Function = struct {
812874 }
813875
814876 fn genAsm(self: *Function, inst: *ir.Inst.Assembly, comptime arch: Target.Cpu.Arch) !MCValue {
877 if (!inst.args.is_volatile and inst.base.isUnused())
878 return MCValue.dead;
815879 if (arch != .x86_64 and arch != .i386) {
816880 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
817881 }
......@@ -880,7 +944,18 @@ const Function = struct {
880944 .none => unreachable,
881945 .unreach => unreachable,
882946 .compare_flags_unsigned => |op| {
883 return self.fail(src, "TODO set register with compare flags value (unsigned)", .{});
947 try self.code.ensureCapacity(self.code.items.len + 3);
948 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
949 const opcode: u8 = switch (op) {
950 .gte => 0x93,
951 .gt => 0x97,
952 .neq => 0x95,
953 .lt => 0x92,
954 .lte => 0x96,
955 .eq => 0x94,
956 };
957 const id = @as(u8, reg.id() & 0b111);
958 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
884959 },
885960 .compare_flags_signed => |op| {
886961 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
......@@ -1135,6 +1210,9 @@ const Function = struct {
11351210 }
11361211 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
11371212 },
1213 .Bool => {
1214 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1215 },
11381216 .ComptimeInt => unreachable, // semantic analysis prevents this
11391217 .ComptimeFloat => unreachable, // semantic analysis prevents this
11401218 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
src-self-hosted/ir.zig+23-8
......@@ -14,30 +14,35 @@ pub const Inst = struct {
1414 tag: Tag,
1515 /// Each bit represents the index of an `Inst` parameter in the `args` field.
1616 /// If a bit is set, it marks the end of the lifetime of the corresponding
17 /// instruction parameter. For example, 0b000_00101 means that the first and
17 /// instruction parameter. For example, 0b101 means that the first and
1818 /// third `Inst` parameters' lifetimes end after this instruction, and will
1919 /// not have any more following references.
2020 /// The most significant bit being set means that the instruction itself is
2121 /// never referenced, in other words its lifetime ends as soon as it finishes.
22 /// If bit 7 (0b1xxx_xxxx) is set, it means this instruction itself is unreferenced.
23 /// If bit 6 (0bx1xx_xxxx) is set, it means this is a special case and the
22 /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced.
23 /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
2424 /// lifetimes of operands are encoded elsewhere.
25 deaths: u8 = undefined,
25 deaths: DeathsInt = undefined,
2626 ty: Type,
2727 /// Byte offset into the source.
2828 src: usize,
2929
30 pub const DeathsInt = u16;
31 pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
32 pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1;
33 pub const deaths_bits = unreferenced_bit_index - 1;
34
3035 pub fn isUnused(self: Inst) bool {
31 return (self.deaths & 0b1000_0000) != 0;
36 return (self.deaths & (1 << unreferenced_bit_index)) != 0;
3237 }
3338
34 pub fn operandDies(self: Inst, index: u3) bool {
35 assert(index < 6);
39 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
40 assert(index < deaths_bits);
3641 return @truncate(u1, self.deaths << index) != 0;
3742 }
3843
3944 pub fn specialOperandDeaths(self: Inst) bool {
40 return (self.deaths & 0b1000_0000) != 0;
45 return (self.deaths & (1 << deaths_bits)) != 0;
4146 }
4247
4348 pub const Tag = enum {
......@@ -60,6 +65,7 @@ pub const Inst = struct {
6065 retvoid,
6166 sub,
6267 unreach,
68 not,
6369 };
6470
6571 pub fn cast(base: *Inst, comptime T: type) ?*T {
......@@ -194,6 +200,15 @@ pub const Inst = struct {
194200 false_death_count: u32 = 0,
195201 };
196202
203 pub const Not = struct {
204 pub const base_tag = Tag.not;
205
206 base: Inst,
207 args: struct {
208 operand: *Inst,
209 },
210 };
211
197212 pub const Constant = struct {
198213 pub const base_tag = Tag.constant;
199214 base: Inst,
src-self-hosted/liveness.zig+27-8
......@@ -34,7 +34,7 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins
3434 inline for (std.meta.declarations(ir.Inst)) |decl| {
3535 switch (decl.data) {
3636 .Type => |T| {
37 if (@hasDecl(T, "base_tag")) {
37 if (@typeInfo(T) == .Struct and @hasDecl(T, "base_tag")) {
3838 if (T.base_tag == base.tag) {
3939 return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base));
4040 }
......@@ -47,7 +47,13 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins
4747}
4848
4949fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), comptime T: type, inst: *T) error{OutOfMemory}!void {
50 inst.base.deaths = 0;
50 if (table.contains(&inst.base)) {
51 inst.base.deaths = 0;
52 } else {
53 // No tombstone for this instruction means it is never referenced,
54 // and its birth marks its own death. Very metal 🤘
55 inst.base.deaths = 1 << ir.Inst.unreferenced_bit_index;
56 }
5157
5258 switch (T) {
5359 ir.Inst.Constant => return,
......@@ -106,15 +112,28 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void
106112 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
107113 // condition's lifetime ends immediately before entering any branch.
108114 },
115 ir.Inst.Call => {
116 // Call instructions have a runtime-known number of operands so we have to handle them ourselves here.
117 const needed_bits = 1 + inst.args.args.len;
118 if (needed_bits <= ir.Inst.deaths_bits) {
119 var bit_i: ir.Inst.DeathsBitIndex = 0;
120 {
121 const prev = try table.fetchPut(inst.args.func, {});
122 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
123 bit_i += 1;
124 }
125 for (inst.args.args) |arg| {
126 const prev = try table.fetchPut(arg, {});
127 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
128 bit_i += 1;
129 }
130 } else {
131 @panic("Handle liveness analysis for function calls with many parameters");
132 }
133 },
109134 else => {},
110135 }
111136
112 if (!table.contains(&inst.base)) {
113 // No tombstone for this instruction means it is never referenced,
114 // and its birth marks its own death. Very metal 🤘
115 inst.base.deaths |= 1 << 7;
116 }
117
118137 const Args = ir.Inst.Args(T);
119138 if (Args == void) {
120139 return;
src-self-hosted/translate_c.zig+80-87
......@@ -1561,7 +1561,7 @@ fn transImplicitCastExpr(
15611561 return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
15621562 }
15631563
1564 const prefix_op = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
1564 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
15651565 prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value);
15661566
15671567 return maybeSuppressResult(rp, scope, result_used, &prefix_op.base);
......@@ -1673,11 +1673,7 @@ fn isBoolRes(res: *ast.Node) bool {
16731673
16741674 else => {},
16751675 },
1676 .PrefixOp => switch (@fieldParentPtr(ast.Node.PrefixOp, "base", res).op) {
1677 .BoolNot => return true,
1678
1679 else => {},
1680 },
1676 .BoolNot => return true,
16811677 .BoolLiteral => return true,
16821678 .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
16831679 else => {},
......@@ -2162,21 +2158,16 @@ fn transCreateNodeArrayType(
21622158 source_loc: ZigClangSourceLocation,
21632159 ty: *const ZigClangType,
21642160 len: anytype,
2165) TransError!*ast.Node {
2166 var node = try transCreateNodePrefixOp(
2167 rp.c,
2168 .{
2169 .ArrayType = .{
2170 .len_expr = undefined,
2171 .sentinel = null,
2172 },
2173 },
2174 .LBracket,
2175 "[",
2176 );
2177 node.op.ArrayType.len_expr = try transCreateNodeInt(rp.c, len);
2161) !*ast.Node {
2162 const node = try rp.c.arena.create(ast.Node.ArrayType);
2163 const op_token = try appendToken(rp.c, .LBracket, "[");
2164 const len_expr = try transCreateNodeInt(rp.c, len);
21782165 _ = try appendToken(rp.c, .RBracket, "]");
2179 node.rhs = try transType(rp, ty, source_loc);
2166 node.* = .{
2167 .op_token = op_token,
2168 .rhs = try transType(rp, ty, source_loc),
2169 .len_expr = len_expr,
2170 };
21802171 return &node.base;
21812172}
21822173
......@@ -2449,7 +2440,7 @@ fn transDoWhileLoop(
24492440 },
24502441 };
24512442 defer cond_scope.deinit();
2452 const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
2443 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
24532444 prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
24542445 _ = try appendToken(rp.c, .RParen, ")");
24552446 if_node.condition = &prefix_op.base;
......@@ -3036,7 +3027,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
30363027 else
30373028 return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
30383029 .AddrOf => {
3039 const op_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3030 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
30403031 op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
30413032 return &op_node.base;
30423033 },
......@@ -3052,7 +3043,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
30523043 .Plus => return transExpr(rp, scope, op_expr, used, .r_value),
30533044 .Minus => {
30543045 if (!qualTypeHasWrappingOverflow(ZigClangExpr_getType(op_expr))) {
3055 const op_node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-");
3046 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .Negation, .Minus, "-");
30563047 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
30573048 return &op_node.base;
30583049 } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) {
......@@ -3065,12 +3056,12 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
30653056 return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{});
30663057 },
30673058 .Not => {
3068 const op_node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~");
3059 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~");
30693060 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
30703061 return &op_node.base;
30713062 },
30723063 .LNot => {
3073 const op_node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
3064 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
30743065 op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);
30753066 return &op_node.base;
30763067 },
......@@ -3116,7 +3107,7 @@ fn transCreatePreCrement(
31163107
31173108 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
31183109 node.eq_token = try appendToken(rp.c, .Equal, "=");
3119 const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3110 const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
31203111 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
31213112 node.init_node = &rhs_node.base;
31223113 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
......@@ -3182,7 +3173,7 @@ fn transCreatePostCrement(
31823173
31833174 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
31843175 node.eq_token = try appendToken(rp.c, .Equal, "=");
3185 const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3176 const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
31863177 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
31873178 node.init_node = &rhs_node.base;
31883179 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
......@@ -3336,7 +3327,7 @@ fn transCreateCompoundAssign(
33363327
33373328 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
33383329 node.eq_token = try appendToken(rp.c, .Equal, "=");
3339 const addr_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3330 const addr_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
33403331 addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value);
33413332 node.init_node = &addr_node.base;
33423333 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
......@@ -3984,16 +3975,15 @@ fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []c
39843975 return &field_access_node.base;
39853976}
39863977
3987fn transCreateNodePrefixOp(
3978fn transCreateNodeSimplePrefixOp(
39883979 c: *Context,
3989 op: ast.Node.PrefixOp.Op,
3980 comptime tag: ast.Node.Id,
39903981 op_tok_id: std.zig.Token.Id,
39913982 bytes: []const u8,
3992) !*ast.Node.PrefixOp {
3993 const node = try c.arena.create(ast.Node.PrefixOp);
3983) !*ast.Node.SimplePrefixOp(tag) {
3984 const node = try c.arena.create(ast.Node.SimplePrefixOp(tag));
39943985 node.* = .{
39953986 .op_token = try appendToken(c, op_tok_id, bytes),
3996 .op = op,
39973987 .rhs = undefined, // translate and set afterward
39983988 };
39993989 return node;
......@@ -4065,8 +4055,8 @@ fn transCreateNodePtrType(
40654055 is_const: bool,
40664056 is_volatile: bool,
40674057 op_tok_id: std.zig.Token.Id,
4068) !*ast.Node.PrefixOp {
4069 const node = try c.arena.create(ast.Node.PrefixOp);
4058) !*ast.Node.PtrType {
4059 const node = try c.arena.create(ast.Node.PtrType);
40704060 const op_token = switch (op_tok_id) {
40714061 .LBracket => blk: {
40724062 const lbracket = try appendToken(c, .LBracket, "[");
......@@ -4086,11 +4076,9 @@ fn transCreateNodePtrType(
40864076 };
40874077 node.* = .{
40884078 .op_token = op_token,
4089 .op = .{
4090 .PtrType = .{
4091 .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
4092 .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
4093 },
4079 .ptr_info = .{
4080 .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
4081 .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
40944082 },
40954083 .rhs = undefined, // translate and set afterward
40964084 };
......@@ -4569,12 +4557,12 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
45694557 .Pointer => {
45704558 const child_qt = ZigClangType_getPointeeType(ty);
45714559 if (qualTypeChildIsFnProto(child_qt)) {
4572 const optional_node = try transCreateNodePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
4560 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
45734561 optional_node.rhs = try transQualType(rp, child_qt, source_loc);
45744562 return &optional_node.base;
45754563 }
45764564 if (typeIsOpaque(rp.c, ZigClangQualType_getTypePtr(child_qt), source_loc)) {
4577 const optional_node = try transCreateNodePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
4565 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
45784566 const pointer_node = try transCreateNodePtrType(
45794567 rp.c,
45804568 ZigClangQualType_isConstQualified(child_qt),
......@@ -4599,21 +4587,8 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
45994587
46004588 const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);
46014589 const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));
4602 var node = try transCreateNodePrefixOp(
4603 rp.c,
4604 .{
4605 .ArrayType = .{
4606 .len_expr = undefined,
4607 .sentinel = null,
4608 },
4609 },
4610 .LBracket,
4611 "[",
4612 );
4613 node.op.ArrayType.len_expr = try transCreateNodeInt(rp.c, size);
4614 _ = try appendToken(rp.c, .RBracket, "]");
4615 node.rhs = try transQualType(rp, ZigClangConstantArrayType_getElementType(const_arr_ty), source_loc);
4616 return &node.base;
4590 const elem_ty = ZigClangQualType_getTypePtr(ZigClangConstantArrayType_getElementType(const_arr_ty));
4591 return try transCreateNodeArrayType(rp, source_loc, elem_ty, size);
46174592 },
46184593 .IncompleteArray => {
46194594 const incomplete_array_ty = @ptrCast(*const ZigClangIncompleteArrayType, ty);
......@@ -5824,7 +5799,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
58245799 if (prev_id == .Keyword_void) {
58255800 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
58265801 ptr.rhs = node;
5827 const optional_node = try transCreateNodePrefixOp(c, .OptionalType, .QuestionMark, "?");
5802 const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?");
58285803 optional_node.rhs = &ptr.base;
58295804 return &optional_node.base;
58305805 } else {
......@@ -5993,18 +5968,18 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
59935968
59945969 switch (op_tok.id) {
59955970 .Bang => {
5996 const node = try transCreateNodePrefixOp(c, .BoolNot, .Bang, "!");
5971 const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
59975972 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
59985973 return &node.base;
59995974 },
60005975 .Minus => {
6001 const node = try transCreateNodePrefixOp(c, .Negation, .Minus, "-");
5976 const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
60025977 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
60035978 return &node.base;
60045979 },
60055980 .Plus => return try parseCPrefixOpExpr(c, it, source, source_loc, scope),
60065981 .Tilde => {
6007 const node = try transCreateNodePrefixOp(c, .BitNot, .Tilde, "~");
5982 const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
60085983 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
60095984 return &node.base;
60105985 },
......@@ -6013,7 +5988,7 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
60135988 return try transCreateNodePtrDeref(c, node);
60145989 },
60155990 .Ampersand => {
6016 const node = try transCreateNodePrefixOp(c, .AddressOf, .Ampersand, "&");
5991 const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
60175992 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
60185993 return &node.base;
60195994 },
......@@ -6034,29 +6009,49 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {
60346009}
60356010
60366011fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
6037 if (node.id == .ContainerDecl) {
6038 return node;
6039 } else if (node.id == .PrefixOp) {
6040 return node;
6041 } else if (node.cast(ast.Node.Identifier)) |ident| {
6042 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
6043 if (value.cast(ast.Node.VarDecl)) |var_decl|
6044 return getContainer(c, var_decl.init_node.?);
6045 }
6046 } else if (node.cast(ast.Node.InfixOp)) |infix| {
6047 if (infix.op != .Period)
6048 return null;
6049 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
6050 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
6051 for (container.fieldsAndDecls()) |field_ref| {
6052 const field = field_ref.cast(ast.Node.ContainerField).?;
6053 const ident = infix.rhs.cast(ast.Node.Identifier).?;
6054 if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
6055 return getContainer(c, field.type_expr.?);
6012 switch (node.id) {
6013 .ContainerDecl,
6014 .AddressOf,
6015 .Await,
6016 .BitNot,
6017 .BoolNot,
6018 .OptionalType,
6019 .Negation,
6020 .NegationWrap,
6021 .Resume,
6022 .Try,
6023 .ArrayType,
6024 .ArrayTypeSentinel,
6025 .PtrType,
6026 .SliceType,
6027 => return node,
6028
6029 .Identifier => {
6030 const ident = node.cast(ast.Node.Identifier).?;
6031 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
6032 if (value.cast(ast.Node.VarDecl)) |var_decl|
6033 return getContainer(c, var_decl.init_node.?);
6034 }
6035 },
6036
6037 .InfixOp => {
6038 const infix = node.cast(ast.Node.InfixOp).?;
6039 if (infix.op != .Period)
6040 return null;
6041 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
6042 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
6043 for (container.fieldsAndDecls()) |field_ref| {
6044 const field = field_ref.cast(ast.Node.ContainerField).?;
6045 const ident = infix.rhs.cast(ast.Node.Identifier).?;
6046 if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
6047 return getContainer(c, field.type_expr.?);
6048 }
60566049 }
60576050 }
60586051 }
6059 }
6052 },
6053
6054 else => {},
60606055 }
60616056 return null;
60626057}
......@@ -6091,11 +6086,9 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
60916086fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
60926087 const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null;
60936088 if (getContainerTypeOf(c, init)) |ty_node| {
6094 if (ty_node.cast(ast.Node.PrefixOp)) |prefix| {
6095 if (prefix.op == .OptionalType) {
6096 if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
6097 return fn_proto;
6098 }
6089 if (ty_node.cast(ast.Node.OptionalType)) |prefix| {
6090 if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
6091 return fn_proto;
60996092 }
61006093 }
61016094 }
src-self-hosted/type.zig+17-2
......@@ -163,6 +163,22 @@ pub const Type = extern union {
163163 return sentinel_b == null;
164164 }
165165 },
166 .Fn => {
167 if (!a.fnReturnType().eql(b.fnReturnType()))
168 return false;
169 if (a.fnCallingConvention() != b.fnCallingConvention())
170 return false;
171 const a_param_len = a.fnParamLen();
172 const b_param_len = b.fnParamLen();
173 if (a_param_len != b_param_len)
174 return false;
175 var i: usize = 0;
176 while (i < a_param_len) : (i += 1) {
177 if (!a.fnParamType(i).eql(b.fnParamType(i)))
178 return false;
179 }
180 return true;
181 },
166182 .Float,
167183 .Struct,
168184 .Optional,
......@@ -170,14 +186,13 @@ pub const Type = extern union {
170186 .ErrorSet,
171187 .Enum,
172188 .Union,
173 .Fn,
174189 .BoundFn,
175190 .Opaque,
176191 .Frame,
177192 .AnyFrame,
178193 .Vector,
179194 .EnumLiteral,
180 => @panic("TODO implement more Type equality comparison"),
195 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
181196 }
182197 }
183198
src-self-hosted/value.zig+23-11
......@@ -427,8 +427,6 @@ pub const Value = extern union {
427427 .fn_ccc_void_no_args_type,
428428 .single_const_pointer_to_comptime_int_type,
429429 .const_slice_u8_type,
430 .bool_true,
431 .bool_false,
432430 .null_value,
433431 .function,
434432 .ref_val,
......@@ -441,8 +439,11 @@ pub const Value = extern union {
441439
442440 .the_one_possible_value, // An integer with one possible value is always zero.
443441 .zero,
442 .bool_false,
444443 => return BigIntMutable.init(&space.limbs, 0).toConst(),
445444
445 .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(),
446
446447 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
447448 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
448449 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
......@@ -493,8 +494,6 @@ pub const Value = extern union {
493494 .fn_ccc_void_no_args_type,
494495 .single_const_pointer_to_comptime_int_type,
495496 .const_slice_u8_type,
496 .bool_true,
497 .bool_false,
498497 .null_value,
499498 .function,
500499 .ref_val,
......@@ -507,8 +506,11 @@ pub const Value = extern union {
507506
508507 .zero,
509508 .the_one_possible_value, // an integer with one possible value is always zero
509 .bool_false,
510510 => return 0,
511511
512 .bool_true => return 1,
513
512514 .int_u64 => return self.cast(Payload.Int_u64).?.int,
513515 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
514516 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
......@@ -560,8 +562,6 @@ pub const Value = extern union {
560562 .fn_ccc_void_no_args_type,
561563 .single_const_pointer_to_comptime_int_type,
562564 .const_slice_u8_type,
563 .bool_true,
564 .bool_false,
565565 .null_value,
566566 .function,
567567 .ref_val,
......@@ -574,8 +574,11 @@ pub const Value = extern union {
574574
575575 .the_one_possible_value, // an integer with one possible value is always zero
576576 .zero,
577 .bool_false,
577578 => return 0,
578579
580 .bool_true => return 1,
581
579582 .int_u64 => {
580583 const x = self.cast(Payload.Int_u64).?.int;
581584 if (x == 0) return 0;
......@@ -632,8 +635,6 @@ pub const Value = extern union {
632635 .fn_ccc_void_no_args_type,
633636 .single_const_pointer_to_comptime_int_type,
634637 .const_slice_u8_type,
635 .bool_true,
636 .bool_false,
637638 .null_value,
638639 .function,
639640 .ref_val,
......@@ -646,8 +647,18 @@ pub const Value = extern union {
646647 .zero,
647648 .undef,
648649 .the_one_possible_value, // an integer with one possible value is always zero
650 .bool_false,
649651 => return true,
650652
653 .bool_true => {
654 const info = ty.intInfo(target);
655 if (info.signed) {
656 return info.bits >= 2;
657 } else {
658 return info.bits >= 1;
659 }
660 },
661
651662 .int_u64 => switch (ty.zigTypeTag()) {
652663 .Int => {
653664 const x = self.cast(Payload.Int_u64).?.int;
......@@ -796,8 +807,6 @@ pub const Value = extern union {
796807 .fn_ccc_void_no_args_type,
797808 .single_const_pointer_to_comptime_int_type,
798809 .const_slice_u8_type,
799 .bool_true,
800 .bool_false,
801810 .null_value,
802811 .function,
803812 .ref_val,
......@@ -810,8 +819,11 @@ pub const Value = extern union {
810819
811820 .zero,
812821 .the_one_possible_value, // an integer with one possible value is always zero
822 .bool_false,
813823 => return .eq,
814824
825 .bool_true => return .gt,
826
815827 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
816828 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
817829 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
......@@ -855,7 +867,7 @@ pub const Value = extern union {
855867 pub fn toBool(self: Value) bool {
856868 return switch (self.tag()) {
857869 .bool_true => true,
858 .bool_false => false,
870 .bool_false, .zero => false,
859871 else => unreachable,
860872 };
861873 }
src-self-hosted/zir.zig+30
......@@ -56,6 +56,7 @@ pub const Inst = struct {
5656 declval,
5757 /// Same as declval but the parameter is a `*Module.Decl` rather than a name.
5858 declval_in_module,
59 boolnot,
5960 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
6061 str,
6162 int,
......@@ -115,6 +116,7 @@ pub const Inst = struct {
115116 .cmp,
116117 .isnull,
117118 .isnonnull,
119 .boolnot,
118120 => false,
119121
120122 .condbr,
......@@ -143,6 +145,7 @@ pub const Inst = struct {
143145 .declval_in_module => DeclValInModule,
144146 .compileerror => CompileError,
145147 .@"const" => Const,
148 .boolnot => BoolNot,
146149 .str => Str,
147150 .int => Int,
148151 .inttype => IntType,
......@@ -299,6 +302,16 @@ pub const Inst = struct {
299302 kw_args: struct {},
300303 };
301304
305 pub const BoolNot = struct {
306 pub const base_tag = Tag.boolnot;
307 base: Inst,
308
309 positionals: struct {
310 operand: *Inst,
311 },
312 kw_args: struct {},
313 };
314
302315 pub const Str = struct {
303316 pub const base_tag = Tag.str;
304317 base: Inst,
......@@ -762,6 +775,7 @@ const Writer = struct {
762775 .declval_in_module => return self.writeInstToStreamGeneric(stream, .declval_in_module, inst),
763776 .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, inst),
764777 .@"const" => return self.writeInstToStreamGeneric(stream, .@"const", inst),
778 .boolnot => return self.writeInstToStreamGeneric(stream, .boolnot, inst),
765779 .str => return self.writeInstToStreamGeneric(stream, .str, inst),
766780 .int => return self.writeInstToStreamGeneric(stream, .int, inst),
767781 .inttype => return self.writeInstToStreamGeneric(stream, .inttype, inst),
......@@ -1658,6 +1672,22 @@ const EmitZIR = struct {
16581672 };
16591673 for (body.instructions) |inst| {
16601674 const new_inst = switch (inst.tag) {
1675 .not => blk: {
1676 const old_inst = inst.cast(ir.Inst.Not).?;
1677 assert(inst.ty.zigTypeTag() == .Bool);
1678 const new_inst = try self.arena.allocator.create(Inst.BoolNot);
1679 new_inst.* = .{
1680 .base = .{
1681 .src = inst.src,
1682 .tag = Inst.BoolNot.base_tag,
1683 },
1684 .positionals = .{
1685 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1686 },
1687 .kw_args = .{},
1688 };
1689 break :blk &new_inst.base;
1690 },
16611691 .add => blk: {
16621692 const old_inst = inst.cast(ir.Inst.Add).?;
16631693 const new_inst = try self.arena.allocator.create(Inst.Add);
test/stage2/compare_output.zig+57
......@@ -170,4 +170,61 @@ pub fn addCases(ctx: *TestContext) !void {
170170 "",
171171 );
172172 }
173 {
174 var case = ctx.exe("assert function", linux_x64);
175 case.addCompareOutput(
176 \\export fn _start() noreturn {
177 \\ add(3, 4);
178 \\
179 \\ exit();
180 \\}
181 \\
182 \\fn add(a: u32, b: u32) void {
183 \\ assert(a + b == 7);
184 \\}
185 \\
186 \\pub fn assert(ok: bool) void {
187 \\ if (!ok) unreachable; // assertion failure
188 \\}
189 \\
190 \\fn exit() noreturn {
191 \\ asm volatile ("syscall"
192 \\ :
193 \\ : [number] "{rax}" (231),
194 \\ [arg1] "{rdi}" (0)
195 \\ : "rcx", "r11", "memory"
196 \\ );
197 \\ unreachable;
198 \\}
199 ,
200 "",
201 );
202 case.addCompareOutput(
203 \\export fn _start() noreturn {
204 \\ add(100, 200);
205 \\
206 \\ exit();
207 \\}
208 \\
209 \\fn add(a: u32, b: u32) void {
210 \\ assert(a + b == 300);
211 \\}
212 \\
213 \\pub fn assert(ok: bool) void {
214 \\ if (!ok) unreachable; // assertion failure
215 \\}
216 \\
217 \\fn exit() noreturn {
218 \\ asm volatile ("syscall"
219 \\ :
220 \\ : [number] "{rax}" (231),
221 \\ [arg1] "{rdi}" (0)
222 \\ : "rcx", "r11", "memory"
223 \\ );
224 \\ unreachable;
225 \\}
226 ,
227 "",
228 );
229 }
173230}