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
signature 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 {...@@ -410,7 +410,20 @@ pub const Node = struct {
410410
411 // Operators411 // Operators
412 InfixOp,412 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,
414 /// Not all suffix operations are under this tag. To save memory, some427 /// Not all suffix operations are under this tag. To save memory, some
415 /// suffix operations have dedicated Node tags.428 /// suffix operations have dedicated Node tags.
416 SuffixOp,429 SuffixOp,
...@@ -1797,85 +1810,116 @@ pub const Node = struct {...@@ -1797,85 +1810,116 @@ pub const Node = struct {
1797 }1810 }
1798 };1811 };
17991812
1800 pub const PrefixOp = struct {1813 pub const AddressOf = SimplePrefixOp(.AddressOf);
1801 base: Node = Node{ .id = .PrefixOp },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 },
1802 op_token: TokenIndex,1848 op_token: TokenIndex,
1803 op: Op,
1804 rhs: *Node,1849 rhs: *Node,
1850 len_expr: *Node,
18051851
1806 pub const Op = union(enum) {1852 pub fn iterate(self: *const ArrayType, index: usize) ?*Node {
1807 AddressOf,1853 var i = index;
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 };
18201854
1821 pub const ArrayInfo = struct {1855 if (i < 1) return self.len_expr;
1822 len_expr: *Node,1856 i -= 1;
1823 sentinel: ?*Node,
1824 };
18251857
1826 pub const PtrInfo = struct {1858 if (i < 1) return self.rhs;
1827 allowzero_token: ?TokenIndex = null,1859 i -= 1;
1828 align_info: ?Align = null,1860
1829 const_token: ?TokenIndex = null,1861 return null;
1830 volatile_token: ?TokenIndex = null,1862 }
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 };
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 {
1845 var i = index;1881 var i = index;
18461882
1847 switch (self.op) {1883 if (i < 1) return self.len_expr;
1848 .PtrType, .SliceType => |addr_of_info| {1884 i -= 1;
1849 if (addr_of_info.sentinel) |sentinel| {
1850 if (i < 1) return sentinel;
1851 i -= 1;
1852 }
18531885
1854 if (addr_of_info.align_info) |align_info| {1886 if (i < 1) return self.sentinel;
1855 if (i < 1) return align_info.node;1887 i -= 1;
1856 i -= 1;
1857 }
1858 },
18591888
1860 .ArrayType => |array_info| {1889 if (i < 1) return self.rhs;
1861 if (i < 1) return array_info.len_expr;1890 i -= 1;
1862 i -= 1;
1863 if (array_info.sentinel) |sentinel| {
1864 if (i < 1) return sentinel;
1865 i -= 1;
1866 }
1867 },
18681891
1869 .AddressOf,1892 return null;
1870 .Await,1893 }
1871 .BitNot,1894
1872 .BoolNot,1895 pub fn firstToken(self: *const ArrayTypeSentinel) TokenIndex {
1873 .OptionalType,1896 return self.op_token;
1874 .Negation,1897 }
1875 .NegationWrap,1898
1876 .Try,1899 pub fn lastToken(self: *const ArrayTypeSentinel) TokenIndex {
1877 .Resume,1900 return self.rhs.lastToken();
1878 => {},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;
1879 }1923 }
18801924
1881 if (i < 1) return self.rhs;1925 if (i < 1) return self.rhs;
...@@ -1884,11 +1928,47 @@ pub const Node = struct {...@@ -1884,11 +1928,47 @@ pub const Node = struct {
1884 return null;1928 return null;
1885 }1929 }
18861930
1887 pub fn firstToken(self: *const PrefixOp) TokenIndex {1931 pub fn firstToken(self: *const PtrType) TokenIndex {
1888 return self.op_token;1932 return self.op_token;
1889 }1933 }
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 {
1892 return self.rhs.lastToken();1972 return self.rhs.lastToken();
1893 }1973 }
1894 };1974 };
...@@ -2797,6 +2877,24 @@ pub const Node = struct {...@@ -2797,6 +2877,24 @@ pub const Node = struct {
2797 };2877 };
2798};2878};
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
2800test "iterate" {2898test "iterate" {
2801 var root = Node.Root{2899 var root = Node.Root{
2802 .base = Node{ .id = Node.Id.Root },2900 .base = Node{ .id = Node.Id.Root },
lib/std/zig/parse.zig+243-105
...@@ -1120,10 +1120,9 @@ const Parser = struct {...@@ -1120,10 +1120,9 @@ const Parser = struct {
1120 const expr_node = try p.expectNode(parseExpr, .{1120 const expr_node = try p.expectNode(parseExpr, .{
1121 .ExpectedExpr = .{ .token = p.tok_i },1121 .ExpectedExpr = .{ .token = p.tok_i },
1122 });1122 });
1123 const node = try p.arena.allocator.create(Node.PrefixOp);1123 const node = try p.arena.allocator.create(Node.Resume);
1124 node.* = .{1124 node.* = .{
1125 .op_token = token,1125 .op_token = token,
1126 .op = .Resume,
1127 .rhs = expr_node,1126 .rhs = expr_node,
1128 };1127 };
1129 return &node.base;1128 return &node.base;
...@@ -2413,24 +2412,25 @@ const Parser = struct {...@@ -2413,24 +2412,25 @@ const Parser = struct {
2413 /// / KEYWORD_await2412 /// / KEYWORD_await
2414 fn parsePrefixOp(p: *Parser) !?*Node {2413 fn parsePrefixOp(p: *Parser) !?*Node {
2415 const token = p.nextToken();2414 const token = p.nextToken();
2416 const op: Node.PrefixOp.Op = switch (p.token_ids[token]) {2415 switch (p.token_ids[token]) {
2417 .Bang => .BoolNot,2416 .Bang => return p.allocSimplePrefixOp(.BoolNot, token),
2418 .Minus => .Negation,2417 .Minus => return p.allocSimplePrefixOp(.Negation, token),
2419 .Tilde => .BitNot,2418 .Tilde => return p.allocSimplePrefixOp(.BitNot, token),
2420 .MinusPercent => .NegationWrap,2419 .MinusPercent => return p.allocSimplePrefixOp(.NegationWrap, token),
2421 .Ampersand => .AddressOf,2420 .Ampersand => return p.allocSimplePrefixOp(.AddressOf, token),
2422 .Keyword_try => .Try,2421 .Keyword_try => return p.allocSimplePrefixOp(.Try, token),
2423 .Keyword_await => .Await,2422 .Keyword_await => return p.allocSimplePrefixOp(.Await, token),
2424 else => {2423 else => {
2425 p.putBackToken(token);2424 p.putBackToken(token);
2426 return null;2425 return null;
2427 },2426 },
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));
2431 node.* = .{2432 node.* = .{
2432 .op_token = token,2433 .op_token = token,
2433 .op = op,
2434 .rhs = undefined, // set by caller2434 .rhs = undefined, // set by caller
2435 };2435 };
2436 return &node.base;2436 return &node.base;
...@@ -2450,19 +2450,14 @@ const Parser = struct {...@@ -2450,19 +2450,14 @@ const Parser = struct {
2450 /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*2450 /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
2451 fn parsePrefixTypeOp(p: *Parser) !?*Node {2451 fn parsePrefixTypeOp(p: *Parser) !?*Node {
2452 if (p.eatToken(.QuestionMark)) |token| {2452 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);
2454 node.* = .{2454 node.* = .{
2455 .op_token = token,2455 .op_token = token,
2456 .op = .OptionalType,
2457 .rhs = undefined, // set by caller2456 .rhs = undefined, // set by caller
2458 };2457 };
2459 return &node.base;2458 return &node.base;
2460 }2459 }
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?
2466 if (p.eatToken(.Keyword_anyframe)) |token| {2461 if (p.eatToken(.Keyword_anyframe)) |token| {
2467 const arrow = p.eatToken(.Arrow) orelse {2462 const arrow = p.eatToken(.Arrow) orelse {
2468 p.putBackToken(token);2463 p.putBackToken(token);
...@@ -2482,11 +2477,15 @@ const Parser = struct {...@@ -2482,11 +2477,15 @@ const Parser = struct {
2482 if (try p.parsePtrTypeStart()) |node| {2477 if (try p.parsePtrTypeStart()) |node| {
2483 // If the token encountered was **, there will be two nodes instead of one.2478 // If the token encountered was **, there will be two nodes instead of one.
2484 // The attributes should be applied to the rightmost operator.2479 // The attributes should be applied to the rightmost operator.
2485 const prefix_op = node.cast(Node.PrefixOp).?;2480 var ptr_info = if (node.cast(Node.PtrType)) |ptr_type|
2486 var ptr_info = if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk)2481 if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk)
2487 &prefix_op.rhs.cast(Node.PrefixOp).?.op.PtrType2482 &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
2488 else2487 else
2489 &prefix_op.op.PtrType;2488 unreachable;
24902489
2491 while (true) {2490 while (true) {
2492 if (p.eatToken(.Keyword_align)) |align_token| {2491 if (p.eatToken(.Keyword_align)) |align_token| {
...@@ -2505,7 +2504,7 @@ const Parser = struct {...@@ -2505,7 +2504,7 @@ const Parser = struct {
2505 .ExpectedIntegerLiteral = .{ .token = p.tok_i },2504 .ExpectedIntegerLiteral = .{ .token = p.tok_i },
2506 });2505 });
25072506
2508 break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{2507 break :bit_range_value ast.PtrInfo.Align.BitRange{
2509 .start = range_start,2508 .start = range_start,
2510 .end = range_end,2509 .end = range_end,
2511 };2510 };
...@@ -2519,7 +2518,7 @@ const Parser = struct {...@@ -2519,7 +2518,7 @@ const Parser = struct {
2519 continue;2518 continue;
2520 }2519 }
25212520
2522 ptr_info.align_info = Node.PrefixOp.PtrInfo.Align{2521 ptr_info.align_info = ast.PtrInfo.Align{
2523 .node = expr_node,2522 .node = expr_node,
2524 .bit_range = bit_range,2523 .bit_range = bit_range,
2525 };2524 };
...@@ -2563,58 +2562,54 @@ const Parser = struct {...@@ -2563,58 +2562,54 @@ const Parser = struct {
2563 }2562 }
25642563
2565 if (try p.parseArrayTypeStart()) |node| {2564 if (try p.parseArrayTypeStart()) |node| {
2566 switch (node.cast(Node.PrefixOp).?.op) {2565 if (node.cast(Node.SliceType)) |slice_type| {
2567 .ArrayType => {},2566 // Collect pointer qualifiers in any order, but disallow duplicates
2568 .SliceType => |*slice_type| {2567 while (true) {
2569 // Collect pointer qualifiers in any order, but disallow duplicates2568 if (try p.parseByteAlign()) |align_expr| {
2570 while (true) {2569 if (slice_type.ptr_info.align_info != null) {
2571 if (try p.parseByteAlign()) |align_expr| {2570 try p.errors.append(p.gpa, .{
2572 if (slice_type.align_info != null) {2571 .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
2573 try p.errors.append(p.gpa, .{2572 });
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 };
2582 continue;2573 continue;
2583 }2574 }
2584 if (p.eatToken(.Keyword_const)) |const_token| {2575 slice_type.ptr_info.align_info = ast.PtrInfo.Align{
2585 if (slice_type.const_token != null) {2576 .node = align_expr,
2586 try p.errors.append(p.gpa, .{2577 .bit_range = null,
2587 .ExtraConstQualifier = .{ .token = p.tok_i - 1 },2578 };
2588 });2579 continue;
2589 continue;2580 }
2590 }2581 if (p.eatToken(.Keyword_const)) |const_token| {
2591 slice_type.const_token = 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 });
2592 continue;2586 continue;
2593 }2587 }
2594 if (p.eatToken(.Keyword_volatile)) |volatile_token| {2588 slice_type.ptr_info.const_token = const_token;
2595 if (slice_type.volatile_token != null) {2589 continue;
2596 try p.errors.append(p.gpa, .{2590 }
2597 .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },2591 if (p.eatToken(.Keyword_volatile)) |volatile_token| {
2598 });2592 if (slice_type.ptr_info.volatile_token != null) {
2599 continue;2593 try p.errors.append(p.gpa, .{
2600 }2594 .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
2601 slice_type.volatile_token = volatile_token;2595 });
2602 continue;2596 continue;
2603 }2597 }
2604 if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {2598 slice_type.ptr_info.volatile_token = volatile_token;
2605 if (slice_type.allowzero_token != null) {2599 continue;
2606 try p.errors.append(p.gpa, .{2600 }
2607 .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },2601 if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
2608 });2602 if (slice_type.ptr_info.allowzero_token != null) {
2609 continue;2603 try p.errors.append(p.gpa, .{
2610 }2604 .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
2611 slice_type.allowzero_token = allowzero_token;2605 });
2612 continue;2606 continue;
2613 }2607 }
2614 break;2608 slice_type.ptr_info.allowzero_token = allowzero_token;
2609 continue;
2615 }2610 }
2616 },2611 break;
2617 else => unreachable,2612 }
2618 }2613 }
2619 return node;2614 return node;
2620 }2615 }
...@@ -2728,29 +2723,32 @@ const Parser = struct {...@@ -2728,29 +2723,32 @@ const Parser = struct {
2728 null;2723 null;
2729 const rbracket = try p.expectToken(.RBracket);2724 const rbracket = try p.expectToken(.RBracket);
27302725
2731 const op: Node.PrefixOp.Op = if (expr) |len_expr|2726 if (expr) |len_expr| {
2732 .{2727 if (sentinel) |s| {
2733 .ArrayType = .{2728 const node = try p.arena.allocator.create(Node.ArrayTypeSentinel);
2729 node.* = .{
2730 .op_token = lbracket,
2731 .rhs = undefined, // set by caller
2734 .len_expr = len_expr,2732 .len_expr = len_expr,
2735 .sentinel = sentinel,2733 .sentinel = s,
2736 },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;
2737 }2744 }
2738 else2745 }
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 };
27482746
2749 const node = try p.arena.allocator.create(Node.PrefixOp);2747 const node = try p.arena.allocator.create(Node.SliceType);
2750 node.* = .{2748 node.* = .{
2751 .op_token = lbracket,2749 .op_token = lbracket,
2752 .op = op,
2753 .rhs = undefined, // set by caller2750 .rhs = undefined, // set by caller
2751 .ptr_info = .{ .sentinel = sentinel },
2754 };2752 };
2755 return &node.base;2753 return &node.base;
2756 }2754 }
...@@ -2768,28 +2766,26 @@ const Parser = struct {...@@ -2768,28 +2766,26 @@ const Parser = struct {
2768 })2766 })
2769 else2767 else
2770 null;2768 null;
2771 const node = try p.arena.allocator.create(Node.PrefixOp);2769 const node = try p.arena.allocator.create(Node.PtrType);
2772 node.* = .{2770 node.* = .{
2773 .op_token = asterisk,2771 .op_token = asterisk,
2774 .op = .{ .PtrType = .{ .sentinel = sentinel } },
2775 .rhs = undefined, // set by caller2772 .rhs = undefined, // set by caller
2773 .ptr_info = .{ .sentinel = sentinel },
2776 };2774 };
2777 return &node.base;2775 return &node.base;
2778 }2776 }
27792777
2780 if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| {2778 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);
2782 node.* = .{2780 node.* = .{
2783 .op_token = double_asterisk,2781 .op_token = double_asterisk,
2784 .op = .{ .PtrType = .{} },
2785 .rhs = undefined, // set by caller2782 .rhs = undefined, // set by caller
2786 };2783 };
27872784
2788 // Special case for **, which is its own token2785 // 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);
2790 child.* = .{2787 child.* = .{
2791 .op_token = double_asterisk,2788 .op_token = double_asterisk,
2792 .op = .{ .PtrType = .{} },
2793 .rhs = undefined, // set by caller2789 .rhs = undefined, // set by caller
2794 };2790 };
2795 node.rhs = &child.base;2791 node.rhs = &child.base;
...@@ -2808,10 +2804,9 @@ const Parser = struct {...@@ -2808,10 +2804,9 @@ const Parser = struct {
2808 p.putBackToken(ident);2804 p.putBackToken(ident);
2809 } else {2805 } else {
2810 _ = try p.expectToken(.RBracket);2806 _ = try p.expectToken(.RBracket);
2811 const node = try p.arena.allocator.create(Node.PrefixOp);2807 const node = try p.arena.allocator.create(Node.PtrType);
2812 node.* = .{2808 node.* = .{
2813 .op_token = lbracket,2809 .op_token = lbracket,
2814 .op = .{ .PtrType = .{} },
2815 .rhs = undefined, // set by caller2810 .rhs = undefined, // set by caller
2816 };2811 };
2817 return &node.base;2812 return &node.base;
...@@ -2824,11 +2819,11 @@ const Parser = struct {...@@ -2824,11 +2819,11 @@ const Parser = struct {
2824 else2819 else
2825 null;2820 null;
2826 _ = try p.expectToken(.RBracket);2821 _ = try p.expectToken(.RBracket);
2827 const node = try p.arena.allocator.create(Node.PrefixOp);2822 const node = try p.arena.allocator.create(Node.PtrType);
2828 node.* = .{2823 node.* = .{
2829 .op_token = lbracket,2824 .op_token = lbracket,
2830 .op = .{ .PtrType = .{ .sentinel = sentinel } },
2831 .rhs = undefined, // set by caller2825 .rhs = undefined, // set by caller
2826 .ptr_info = .{ .sentinel = sentinel },
2832 };2827 };
2833 return &node.base;2828 return &node.base;
2834 }2829 }
...@@ -3146,10 +3141,9 @@ const Parser = struct {...@@ -3146,10 +3141,9 @@ const Parser = struct {
31463141
3147 fn parseTry(p: *Parser) !?*Node {3142 fn parseTry(p: *Parser) !?*Node {
3148 const token = p.eatToken(.Keyword_try) orelse return null;3143 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);
3150 node.* = .{3145 node.* = .{
3151 .op_token = token,3146 .op_token = token,
3152 .op = .Try,
3153 .rhs = undefined, // set by caller3147 .rhs = undefined, // set by caller
3154 };3148 };
3155 return &node.base;3149 return &node.base;
...@@ -3228,15 +3222,87 @@ const Parser = struct {...@@ -3228,15 +3222,87 @@ const Parser = struct {
3228 var rightmost_op = first_op;3222 var rightmost_op = first_op;
3229 while (true) {3223 while (true) {
3230 switch (rightmost_op.id) {3224 switch (rightmost_op.id) {
3231 .PrefixOp => {3225 .AddressOf => {
3232 var prefix_op = rightmost_op.cast(Node.PrefixOp).?;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).?;
3233 // If the token encountered was **, there will be two nodes3299 // If the token encountered was **, there will be two nodes
3234 if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk) {3300 if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) {
3235 rightmost_op = prefix_op.rhs;3301 rightmost_op = ptr_type.rhs;
3236 prefix_op = rightmost_op.cast(Node.PrefixOp).?;3302 ptr_type = rightmost_op.cast(Node.PtrType).?;
3237 }3303 }
3238 if (try opParseFn(p)) |rhs| {3304 if (try opParseFn(p)) |rhs| {
3239 prefix_op.rhs = rhs;3305 ptr_type.rhs = rhs;
3240 rightmost_op = rhs;3306 rightmost_op = rhs;
3241 } else break;3307 } else break;
3242 },3308 },
...@@ -3253,8 +3319,80 @@ const Parser = struct {...@@ -3253,8 +3319,80 @@ const Parser = struct {
32533319
3254 // If any prefix op existed, a child node on the RHS is required3320 // If any prefix op existed, a child node on the RHS is required
3255 switch (rightmost_op.id) {3321 switch (rightmost_op.id) {
3256 .PrefixOp => {3322 .AddressOf => {
3257 const prefix_op = rightmost_op.cast(Node.PrefixOp).?;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).?;
3258 prefix_op.rhs = try p.expectNode(childParseFn, .{3396 prefix_op.rhs = try p.expectNode(childParseFn, .{
3259 .InvalidToken = .{ .token = p.tok_i },3397 .InvalidToken = .{ .token = p.tok_i },
3260 });3398 });
lib/std/zig/render.zig+211-145
...@@ -468,166 +468,192 @@ fn renderExpression(...@@ -468,166 +468,192 @@ fn renderExpression(
468 return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);468 return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);
469 },469 },
470470
471 .PrefixOp => {471 .BitNot => {
472 const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);472 const bit_not = @fieldParentPtr(ast.Node.BitNot, "base", base);
473473 try renderToken(tree, stream, bit_not.op_token, indent, start_col, Space.None);
474 switch (prefix_op_node.op) {474 return renderExpression(allocator, stream, tree, indent, start_col, bit_not.rhs, space);
475 .PtrType => |ptr_info| {475 },
476 const op_tok_id = tree.token_ids[prefix_op_node.op_token];476 .BoolNot => {
477 switch (op_tok_id) {477 const bool_not = @fieldParentPtr(ast.Node.BoolNot, "base", base);
478 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),478 try renderToken(tree, stream, bool_not.op_token, indent, start_col, Space.None);
479 .LBracket => if (tree.token_ids[prefix_op_node.op_token + 2] == .Identifier)479 return renderExpression(allocator, stream, tree, indent, start_col, bool_not.rhs, space);
480 try stream.writeAll("[*c")480 },
481 else481 .Negation => {
482 try stream.writeAll("[*"),482 const negation = @fieldParentPtr(ast.Node.Negation, "base", base);
483 else => unreachable,483 try renderToken(tree, stream, negation.op_token, indent, start_col, Space.None);
484 }484 return renderExpression(allocator, stream, tree, indent, start_col, negation.rhs, space);
485 if (ptr_info.sentinel) |sentinel| {485 },
486 const colon_token = tree.prevToken(sentinel.firstToken());486 .NegationWrap => {
487 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :487 const negation_wrap = @fieldParentPtr(ast.Node.NegationWrap, "base", base);
488 const sentinel_space = switch (op_tok_id) {488 try renderToken(tree, stream, negation_wrap.op_token, indent, start_col, Space.None);
489 .LBracket => Space.None,489 return renderExpression(allocator, stream, tree, indent, start_col, negation_wrap.rhs, space);
490 else => Space.Space,490 },
491 };491 .OptionalType => {
492 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space);492 const opt_type = @fieldParentPtr(ast.Node.OptionalType, "base", base);
493 }493 try renderToken(tree, stream, opt_type.op_token, indent, start_col, Space.None);
494 switch (op_tok_id) {494 return renderExpression(allocator, stream, tree, indent, start_col, opt_type.rhs, space);
495 .Asterisk, .AsteriskAsterisk => {},495 },
496 .LBracket => try stream.writeByte(']'),496 .AddressOf => {
497 else => unreachable,497 const addr_of = @fieldParentPtr(ast.Node.AddressOf, "base", base);
498 }498 try renderToken(tree, stream, addr_of.op_token, indent, start_col, Space.None);
499 if (ptr_info.allowzero_token) |allowzero_token| {499 return renderExpression(allocator, stream, tree, indent, start_col, addr_of.rhs, space);
500 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero500 },
501 }501 .Try => {
502 if (ptr_info.align_info) |align_info| {502 const try_node = @fieldParentPtr(ast.Node.Try, "base", base);
503 const lparen_token = tree.prevToken(align_info.node.firstToken());503 try renderToken(tree, stream, try_node.op_token, indent, start_col, Space.Space);
504 const align_token = tree.prevToken(lparen_token);504 return renderExpression(allocator, stream, tree, indent, start_col, try_node.rhs, space);
505505 },
506 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align506 .Resume => {
507 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (507 const resume_node = @fieldParentPtr(ast.Node.Resume, "base", base);
508508 try renderToken(tree, stream, resume_node.op_token, indent, start_col, Space.Space);
509 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);509 return renderExpression(allocator, stream, tree, indent, start_col, resume_node.rhs, space);
510510 },
511 if (align_info.bit_range) |bit_range| {511 .Await => {
512 const colon1 = tree.prevToken(bit_range.start.firstToken());512 const await_node = @fieldParentPtr(ast.Node.Await, "base", base);
513 const colon2 = tree.prevToken(bit_range.end.firstToken());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); // :517 .ArrayType => {
516 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);518 const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base);
517 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :519 return renderArrayType(
518 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);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());548 .PtrType => {
521 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )549 const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base);
522 } else {550 const op_tok_id = tree.token_ids[ptr_type.op_token];
523 const rparen_token = tree.nextToken(align_info.node.lastToken());551 switch (op_tok_id) {
524 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )552 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
525 }553 .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier)
526 }554 try stream.writeAll("[*c")
527 if (ptr_info.const_token) |const_token| {555 else
528 try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const556 try stream.writeAll("[*"),
529 }557 else => unreachable,
530 if (ptr_info.volatile_token) |volatile_token| {558 }
531 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile559 if (ptr_type.ptr_info.sentinel) |sentinel| {
532 }560 const colon_token = tree.prevToken(sentinel.firstToken());
533 },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| {580 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
536 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [581 try renderToken(tree, stream, lparen_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 }
545582
546 if (ptr_info.allowzero_token) |allowzero_token| {583 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
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);
552584
553 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align585 if (align_info.bit_range) |bit_range| {
554 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (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| {594 const rparen_token = tree.nextToken(bit_range.end.lastToken());
559 const colon1 = tree.prevToken(bit_range.start.firstToken());595 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
560 const colon2 = tree.prevToken(bit_range.end.firstToken());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); // :610 .SliceType => {
563 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);611 const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base);
564 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :612 try renderToken(tree, stream, slice_type.op_token, indent, start_col, Space.None); // [
565 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, 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());622 if (slice_type.ptr_info.allowzero_token) |allowzero_token| {
568 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )623 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
569 } else {624 }
570 const rparen_token = tree.nextToken(align_info.node.lastToken());625 if (slice_type.ptr_info.align_info) |align_info| {
571 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )626 const lparen_token = tree.prevToken(align_info.node.firstToken());
572 }627 const align_token = tree.prevToken(lparen_token);
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 },
581628
582 .ArrayType => |array_info| {629 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
583 const lbracket = prefix_op_node.op_token;630 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
584 const rbracket = tree.nextToken(if (array_info.sentinel) |sentinel|
585 sentinel.lastToken()
586 else
587 array_info.len_expr.lastToken());
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;634 if (align_info.bit_range) |bit_range| {
592 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;635 const colon1 = tree.prevToken(bit_range.start.firstToken());
593 const new_indent = if (ends_with_comment) indent + indent_delta else indent;636 const colon2 = tree.prevToken(bit_range.end.firstToken());
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 },
618637
619 .Try,638 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
620 .Resume,639 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
621 => {640 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
622 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);641 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
623 },
624642
625 .Await => |await_info| {643 const rparen_token = tree.nextToken(bit_range.end.lastToken());
626 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);644 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
627 },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 }
628 }649 }
629650 if (slice_type.ptr_info.const_token) |const_token| {
630 return renderExpression(allocator, stream, tree, indent, start_col, prefix_op_node.rhs, space);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);
631 },657 },
632658
633 .ArrayInitializer, .ArrayInitializerDot => {659 .ArrayInitializer, .ArrayInitializerDot => {
...@@ -2057,6 +2083,46 @@ fn renderExpression(...@@ -2057,6 +2083,46 @@ fn renderExpression(
2057 }2083 }
2058}2084}
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
2060fn renderAsmOutput(2126fn renderAsmOutput(
2061 allocator: *mem.Allocator,2127 allocator: *mem.Allocator,
2062 stream: anytype,2128 stream: anytype,
src-self-hosted/Module.zig+48-23
...@@ -36,7 +36,7 @@ bin_file_path: []const u8,...@@ -36,7 +36,7 @@ bin_file_path: []const u8,
36decl_exports: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},36decl_exports: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
37/// We track which export is associated with the given symbol name for quick37/// We track which export is associated with the given symbol name for quick
38/// detection of symbol collisions.38/// detection of symbol collisions.
39symbol_exports: std.StringHashMap(*Export),39symbol_exports: std.StringHashMapUnmanaged(*Export) = .{},
40/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl40/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
41/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that41/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
42/// is performing the export of another Decl.42/// is performing the export of another Decl.
...@@ -769,7 +769,6 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -769,7 +769,6 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
769 .bin_file_path = options.bin_file_path,769 .bin_file_path = options.bin_file_path,
770 .bin_file = bin_file,770 .bin_file = bin_file,
771 .optimize_mode = options.optimize_mode,771 .optimize_mode = options.optimize_mode,
772 .symbol_exports = std.StringHashMap(*Export).init(gpa),
773 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),772 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
774 .keep_source_files_loaded = options.keep_source_files_loaded,773 .keep_source_files_loaded = options.keep_source_files_loaded,
775 };774 };
...@@ -812,7 +811,7 @@ pub fn deinit(self: *Module) void {...@@ -812,7 +811,7 @@ pub fn deinit(self: *Module) void {
812 }811 }
813 self.export_owners.deinit(gpa);812 self.export_owners.deinit(gpa);
814813
815 self.symbol_exports.deinit();814 self.symbol_exports.deinit(gpa);
816 self.root_scope.destroy(gpa);815 self.root_scope.destroy(gpa);
817 self.* = undefined;816 self.* = undefined;
818}817}
...@@ -1309,10 +1308,18 @@ fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir...@@ -1309,10 +1308,18 @@ fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir
1309 .ControlFlowExpression => return self.astGenControlFlowExpression(scope, @fieldParentPtr(ast.Node.ControlFlowExpression, "base", ast_node)),1308 .ControlFlowExpression => return self.astGenControlFlowExpression(scope, @fieldParentPtr(ast.Node.ControlFlowExpression, "base", ast_node)),
1310 .If => return self.astGenIf(scope, @fieldParentPtr(ast.Node.If, "base", ast_node)),1309 .If => return self.astGenIf(scope, @fieldParentPtr(ast.Node.If, "base", ast_node)),
1311 .InfixOp => return self.astGenInfixOp(scope, @fieldParentPtr(ast.Node.InfixOp, "base", ast_node)),1310 .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)),
1312 else => return self.failNode(scope, ast_node, "TODO implement astGenExpr for {}", .{@tagName(ast_node.id)}),1312 else => return self.failNode(scope, ast_node, "TODO implement astGenExpr for {}", .{@tagName(ast_node.id)}),
1313 }1313 }
1314}1314}
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
1316fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) InnerError!*zir.Inst {1323fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) InnerError!*zir.Inst {
1317 switch (infix_node.op) {1324 switch (infix_node.op) {
1318 .Assign => {1325 .Assign => {
...@@ -1351,17 +1358,19 @@ fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) In...@@ -1351,17 +1358,19 @@ fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) In
1351 const tree = scope.tree();1358 const tree = scope.tree();
1352 const src = tree.token_locs[infix_node.op_token].start;1359 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
1354 return self.addZIRInst(scope, src, zir.Inst.Cmp, .{1371 return self.addZIRInst(scope, src, zir.Inst.Cmp, .{
1355 .lhs = lhs,1372 .lhs = lhs,
1356 .op = @as(std.math.CompareOperator, switch (infix_node.op) {1373 .op = op,
1357 .BangEqual => .neq,
1358 .EqualEqual => .eq,
1359 .GreaterThan => .gt,
1360 .GreaterOrEqual => .gte,
1361 .LessThan => .lt,
1362 .LessOrEqual => .lte,
1363 else => unreachable,
1364 }),
1365 .rhs = rhs,1374 .rhs = rhs,
1366 }, .{});1375 }, .{});
1367 },1376 },
...@@ -1408,11 +1417,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir...@@ -1408,11 +1417,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir
1408 defer then_scope.instructions.deinit(self.gpa);1417 defer then_scope.instructions.deinit(self.gpa);
14091418
1410 const then_result = try self.astGenExpr(&then_scope.base, if_node.body);1419 const then_result = try self.astGenExpr(&then_scope.base, if_node.body);
1411 const then_src = tree.token_locs[if_node.body.lastToken()].start;1420 if (!then_result.tag.isNoReturn()) {
1412 _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{1421 const then_src = tree.token_locs[if_node.body.lastToken()].start;
1413 .block = block,1422 _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
1414 .operand = then_result,1423 .block = block,
1415 }, .{});1424 .operand = then_result,
1425 }, .{});
1426 }
1416 condbr.positionals.true_body = .{1427 condbr.positionals.true_body = .{
1417 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),1428 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
1418 };1429 };
...@@ -1426,11 +1437,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir...@@ -1426,11 +1437,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir
14261437
1427 if (if_node.@"else") |else_node| {1438 if (if_node.@"else") |else_node| {
1428 const else_result = try self.astGenExpr(&else_scope.base, else_node.body);1439 const else_result = try self.astGenExpr(&else_scope.base, else_node.body);
1429 const else_src = tree.token_locs[else_node.body.lastToken()].start;1440 if (!else_result.tag.isNoReturn()) {
1430 _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{1441 const else_src = tree.token_locs[else_node.body.lastToken()].start;
1431 .block = block,1442 _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
1432 .operand = else_result,1443 .block = block,
1433 }, .{});1444 .operand = else_result,
1445 }, .{});
1446 }
1434 } else {1447 } else {
1435 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here1448 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
1436 // by directly allocating the body for this one instruction.1449 // by directly allocating the body for this one instruction.
...@@ -2305,7 +2318,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const...@@ -2305,7 +2318,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
2305 return;2318 return;
2306 }2319 }
23072320
2308 try self.symbol_exports.putNoClobber(symbol_name, new_export);2321 try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export);
2309 self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {2322 self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
2310 error.OutOfMemory => return error.OutOfMemory,2323 error.OutOfMemory => return error.OutOfMemory,
2311 else => {2324 else => {
...@@ -2559,6 +2572,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In...@@ -2559,6 +2572,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
2559 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?),2572 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?),
2560 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?),2573 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?),
2561 .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(zir.Inst.IsNonNull).?),2574 .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(zir.Inst.IsNonNull).?),
2575 .boolnot => return self.analyzeInstBoolNot(scope, old_inst.cast(zir.Inst.BoolNot).?),
2562 }2576 }
2563}2577}
25642578
...@@ -3236,6 +3250,17 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!...@@ -3236,6 +3250,17 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!
3236 return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});3250 return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
3237}3251}
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
3239fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {3264fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {
3240 const operand = try self.resolveInst(scope, inst.positionals.operand);3265 const operand = try self.resolveInst(scope, inst.positionals.operand);
3241 return self.analyzeIsNull(scope, inst.base.src, operand, true);3266 return self.analyzeIsNull(scope, inst.base.src, operand, true);
src-self-hosted/codegen.zig+82-4
...@@ -407,6 +407,55 @@ const Function = struct {...@@ -407,6 +407,55 @@ const Function = struct {
407 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),407 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),
408 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?, arch),408 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?, arch),
409 .unreach => return MCValue{ .unreach = {} },409 .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}),
410 }459 }
411 }460 }
412461
...@@ -434,7 +483,7 @@ const Function = struct {...@@ -434,7 +483,7 @@ const Function = struct {
434 }483 }
435 }484 }
436485
437 /// ADD, SUB486 /// ADD, SUB, XOR, OR, AND
438 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {487 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
439 try self.code.ensureCapacity(self.code.items.len + 8);488 try self.code.ensureCapacity(self.code.items.len + 8);
440489
...@@ -695,7 +744,7 @@ const Function = struct {...@@ -695,7 +744,7 @@ const Function = struct {
695744
696 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {745 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
697 switch (arch) {746 switch (arch) {
698 .i386, .x86_64 => {747 .x86_64 => {
699 try self.code.ensureCapacity(self.code.items.len + 6);748 try self.code.ensureCapacity(self.code.items.len + 6);
700749
701 const cond = try self.resolveInst(inst.args.condition);750 const cond = try self.resolveInst(inst.args.condition);
...@@ -724,7 +773,20 @@ const Function = struct {...@@ -724,7 +773,20 @@ const Function = struct {
724 };773 };
725 return self.genX86CondBr(inst, opcode, arch);774 return self.genX86CondBr(inst, opcode, arch);
726 },775 },
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) }),
728 }790 }
729 },791 },
730 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),792 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
...@@ -812,6 +874,8 @@ const Function = struct {...@@ -812,6 +874,8 @@ const Function = struct {
812 }874 }
813875
814 fn genAsm(self: *Function, inst: *ir.Inst.Assembly, comptime arch: Target.Cpu.Arch) !MCValue {876 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;
815 if (arch != .x86_64 and arch != .i386) {879 if (arch != .x86_64 and arch != .i386) {
816 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});880 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
817 }881 }
...@@ -880,7 +944,18 @@ const Function = struct {...@@ -880,7 +944,18 @@ const Function = struct {
880 .none => unreachable,944 .none => unreachable,
881 .unreach => unreachable,945 .unreach => unreachable,
882 .compare_flags_unsigned => |op| {946 .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 });
884 },959 },
885 .compare_flags_signed => |op| {960 .compare_flags_signed => |op| {
886 return self.fail(src, "TODO set register with compare flags value (signed)", .{});961 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
...@@ -1135,6 +1210,9 @@ const Function = struct {...@@ -1135,6 +1210,9 @@ const Function = struct {
1135 }1210 }
1136 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };1211 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
1137 },1212 },
1213 .Bool => {
1214 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1215 },
1138 .ComptimeInt => unreachable, // semantic analysis prevents this1216 .ComptimeInt => unreachable, // semantic analysis prevents this
1139 .ComptimeFloat => unreachable, // semantic analysis prevents this1217 .ComptimeFloat => unreachable, // semantic analysis prevents this
1140 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),1218 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 {...@@ -14,30 +14,35 @@ pub const Inst = struct {
14 tag: Tag,14 tag: Tag,
15 /// Each bit represents the index of an `Inst` parameter in the `args` field.15 /// Each bit represents the index of an `Inst` parameter in the `args` field.
16 /// If a bit is set, it marks the end of the lifetime of the corresponding16 /// 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 and17 /// instruction parameter. For example, 0b101 means that the first and
18 /// third `Inst` parameters' lifetimes end after this instruction, and will18 /// third `Inst` parameters' lifetimes end after this instruction, and will
19 /// not have any more following references.19 /// not have any more following references.
20 /// The most significant bit being set means that the instruction itself is20 /// The most significant bit being set means that the instruction itself is
21 /// never referenced, in other words its lifetime ends as soon as it finishes.21 /// 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.22 /// If bit 15 (0b1xxx_xxxx_xxxx_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 the23 /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
24 /// lifetimes of operands are encoded elsewhere.24 /// lifetimes of operands are encoded elsewhere.
25 deaths: u8 = undefined,25 deaths: DeathsInt = undefined,
26 ty: Type,26 ty: Type,
27 /// Byte offset into the source.27 /// Byte offset into the source.
28 src: usize,28 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
30 pub fn isUnused(self: Inst) bool {35 pub fn isUnused(self: Inst) bool {
31 return (self.deaths & 0b1000_0000) != 0;36 return (self.deaths & (1 << unreferenced_bit_index)) != 0;
32 }37 }
3338
34 pub fn operandDies(self: Inst, index: u3) bool {39 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
35 assert(index < 6);40 assert(index < deaths_bits);
36 return @truncate(u1, self.deaths << index) != 0;41 return @truncate(u1, self.deaths << index) != 0;
37 }42 }
3843
39 pub fn specialOperandDeaths(self: Inst) bool {44 pub fn specialOperandDeaths(self: Inst) bool {
40 return (self.deaths & 0b1000_0000) != 0;45 return (self.deaths & (1 << deaths_bits)) != 0;
41 }46 }
4247
43 pub const Tag = enum {48 pub const Tag = enum {
...@@ -60,6 +65,7 @@ pub const Inst = struct {...@@ -60,6 +65,7 @@ pub const Inst = struct {
60 retvoid,65 retvoid,
61 sub,66 sub,
62 unreach,67 unreach,
68 not,
63 };69 };
6470
65 pub fn cast(base: *Inst, comptime T: type) ?*T {71 pub fn cast(base: *Inst, comptime T: type) ?*T {
...@@ -194,6 +200,15 @@ pub const Inst = struct {...@@ -194,6 +200,15 @@ pub const Inst = struct {
194 false_death_count: u32 = 0,200 false_death_count: u32 = 0,
195 };201 };
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
197 pub const Constant = struct {212 pub const Constant = struct {
198 pub const base_tag = Tag.constant;213 pub const base_tag = Tag.constant;
199 base: Inst,214 base: Inst,
src-self-hosted/liveness.zig+27-8
...@@ -34,7 +34,7 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins...@@ -34,7 +34,7 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins
34 inline for (std.meta.declarations(ir.Inst)) |decl| {34 inline for (std.meta.declarations(ir.Inst)) |decl| {
35 switch (decl.data) {35 switch (decl.data) {
36 .Type => |T| {36 .Type => |T| {
37 if (@hasDecl(T, "base_tag")) {37 if (@typeInfo(T) == .Struct and @hasDecl(T, "base_tag")) {
38 if (T.base_tag == base.tag) {38 if (T.base_tag == base.tag) {
39 return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base));39 return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base));
40 }40 }
...@@ -47,7 +47,13 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins...@@ -47,7 +47,13 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins
47}47}
4848
49fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), comptime T: type, inst: *T) error{OutOfMemory}!void {49fn 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
52 switch (T) {58 switch (T) {
53 ir.Inst.Constant => return,59 ir.Inst.Constant => return,
...@@ -106,15 +112,28 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void...@@ -106,15 +112,28 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void
106 // instruction, and the deaths flag for the CondBr instruction will indicate whether the112 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
107 // condition's lifetime ends immediately before entering any branch.113 // condition's lifetime ends immediately before entering any branch.
108 },114 },
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 },
109 else => {},134 else => {},
110 }135 }
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
118 const Args = ir.Inst.Args(T);137 const Args = ir.Inst.Args(T);
119 if (Args == void) {138 if (Args == void) {
120 return;139 return;
src-self-hosted/translate_c.zig+80-87
...@@ -1561,7 +1561,7 @@ fn transImplicitCastExpr(...@@ -1561,7 +1561,7 @@ fn transImplicitCastExpr(
1561 return maybeSuppressResult(rp, scope, result_used, sub_expr_node);1561 return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
1562 }1562 }
15631563
1564 const prefix_op = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");1564 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
1565 prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value);1565 prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value);
15661566
1567 return maybeSuppressResult(rp, scope, result_used, &prefix_op.base);1567 return maybeSuppressResult(rp, scope, result_used, &prefix_op.base);
...@@ -1673,11 +1673,7 @@ fn isBoolRes(res: *ast.Node) bool {...@@ -1673,11 +1673,7 @@ fn isBoolRes(res: *ast.Node) bool {
16731673
1674 else => {},1674 else => {},
1675 },1675 },
1676 .PrefixOp => switch (@fieldParentPtr(ast.Node.PrefixOp, "base", res).op) {1676 .BoolNot => return true,
1677 .BoolNot => return true,
1678
1679 else => {},
1680 },
1681 .BoolLiteral => return true,1677 .BoolLiteral => return true,
1682 .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),1678 .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
1683 else => {},1679 else => {},
...@@ -2162,21 +2158,16 @@ fn transCreateNodeArrayType(...@@ -2162,21 +2158,16 @@ fn transCreateNodeArrayType(
2162 source_loc: ZigClangSourceLocation,2158 source_loc: ZigClangSourceLocation,
2163 ty: *const ZigClangType,2159 ty: *const ZigClangType,
2164 len: anytype,2160 len: anytype,
2165) TransError!*ast.Node {2161) !*ast.Node {
2166 var node = try transCreateNodePrefixOp(2162 const node = try rp.c.arena.create(ast.Node.ArrayType);
2167 rp.c,2163 const op_token = try appendToken(rp.c, .LBracket, "[");
2168 .{2164 const len_expr = try transCreateNodeInt(rp.c, len);
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);
2178 _ = try appendToken(rp.c, .RBracket, "]");2165 _ = 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 };
2180 return &node.base;2171 return &node.base;
2181}2172}
21822173
...@@ -2449,7 +2440,7 @@ fn transDoWhileLoop(...@@ -2449,7 +2440,7 @@ fn transDoWhileLoop(
2449 },2440 },
2450 };2441 };
2451 defer cond_scope.deinit();2442 defer cond_scope.deinit();
2452 const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");2443 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
2453 prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);2444 prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
2454 _ = try appendToken(rp.c, .RParen, ")");2445 _ = try appendToken(rp.c, .RParen, ")");
2455 if_node.condition = &prefix_op.base;2446 if_node.condition = &prefix_op.base;
...@@ -3036,7 +3027,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar...@@ -3036,7 +3027,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
3036 else3027 else
3037 return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),3028 return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
3038 .AddrOf => {3029 .AddrOf => {
3039 const op_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");3030 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3040 op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);3031 op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
3041 return &op_node.base;3032 return &op_node.base;
3042 },3033 },
...@@ -3052,7 +3043,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar...@@ -3052,7 +3043,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
3052 .Plus => return transExpr(rp, scope, op_expr, used, .r_value),3043 .Plus => return transExpr(rp, scope, op_expr, used, .r_value),
3053 .Minus => {3044 .Minus => {
3054 if (!qualTypeHasWrappingOverflow(ZigClangExpr_getType(op_expr))) {3045 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, "-");
3056 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);3047 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3057 return &op_node.base;3048 return &op_node.base;
3058 } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) {3049 } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) {
...@@ -3065,12 +3056,12 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar...@@ -3065,12 +3056,12 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
3065 return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{});3056 return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{});
3066 },3057 },
3067 .Not => {3058 .Not => {
3068 const op_node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~");3059 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~");
3069 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);3060 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3070 return &op_node.base;3061 return &op_node.base;
3071 },3062 },
3072 .LNot => {3063 .LNot => {
3073 const op_node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");3064 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
3074 op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);3065 op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);
3075 return &op_node.base;3066 return &op_node.base;
3076 },3067 },
...@@ -3116,7 +3107,7 @@ fn transCreatePreCrement(...@@ -3116,7 +3107,7 @@ fn transCreatePreCrement(
31163107
3117 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);3108 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
3118 node.eq_token = try appendToken(rp.c, .Equal, "=");3109 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, "&");
3120 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);3111 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3121 node.init_node = &rhs_node.base;3112 node.init_node = &rhs_node.base;
3122 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");3113 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
...@@ -3182,7 +3173,7 @@ fn transCreatePostCrement(...@@ -3182,7 +3173,7 @@ fn transCreatePostCrement(
31823173
3183 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);3174 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
3184 node.eq_token = try appendToken(rp.c, .Equal, "=");3175 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, "&");
3186 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);3177 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3187 node.init_node = &rhs_node.base;3178 node.init_node = &rhs_node.base;
3188 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");3179 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
...@@ -3336,7 +3327,7 @@ fn transCreateCompoundAssign(...@@ -3336,7 +3327,7 @@ fn transCreateCompoundAssign(
33363327
3337 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);3328 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
3338 node.eq_token = try appendToken(rp.c, .Equal, "=");3329 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, "&");
3340 addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value);3331 addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value);
3341 node.init_node = &addr_node.base;3332 node.init_node = &addr_node.base;
3342 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");3333 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
...@@ -3984,16 +3975,15 @@ fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []c...@@ -3984,16 +3975,15 @@ fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []c
3984 return &field_access_node.base;3975 return &field_access_node.base;
3985}3976}
39863977
3987fn transCreateNodePrefixOp(3978fn transCreateNodeSimplePrefixOp(
3988 c: *Context,3979 c: *Context,
3989 op: ast.Node.PrefixOp.Op,3980 comptime tag: ast.Node.Id,
3990 op_tok_id: std.zig.Token.Id,3981 op_tok_id: std.zig.Token.Id,
3991 bytes: []const u8,3982 bytes: []const u8,
3992) !*ast.Node.PrefixOp {3983) !*ast.Node.SimplePrefixOp(tag) {
3993 const node = try c.arena.create(ast.Node.PrefixOp);3984 const node = try c.arena.create(ast.Node.SimplePrefixOp(tag));
3994 node.* = .{3985 node.* = .{
3995 .op_token = try appendToken(c, op_tok_id, bytes),3986 .op_token = try appendToken(c, op_tok_id, bytes),
3996 .op = op,
3997 .rhs = undefined, // translate and set afterward3987 .rhs = undefined, // translate and set afterward
3998 };3988 };
3999 return node;3989 return node;
...@@ -4065,8 +4055,8 @@ fn transCreateNodePtrType(...@@ -4065,8 +4055,8 @@ fn transCreateNodePtrType(
4065 is_const: bool,4055 is_const: bool,
4066 is_volatile: bool,4056 is_volatile: bool,
4067 op_tok_id: std.zig.Token.Id,4057 op_tok_id: std.zig.Token.Id,
4068) !*ast.Node.PrefixOp {4058) !*ast.Node.PtrType {
4069 const node = try c.arena.create(ast.Node.PrefixOp);4059 const node = try c.arena.create(ast.Node.PtrType);
4070 const op_token = switch (op_tok_id) {4060 const op_token = switch (op_tok_id) {
4071 .LBracket => blk: {4061 .LBracket => blk: {
4072 const lbracket = try appendToken(c, .LBracket, "[");4062 const lbracket = try appendToken(c, .LBracket, "[");
...@@ -4086,11 +4076,9 @@ fn transCreateNodePtrType(...@@ -4086,11 +4076,9 @@ fn transCreateNodePtrType(
4086 };4076 };
4087 node.* = .{4077 node.* = .{
4088 .op_token = op_token,4078 .op_token = op_token,
4089 .op = .{4079 .ptr_info = .{
4090 .PtrType = .{4080 .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
4091 .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,
4092 .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
4093 },
4094 },4082 },
4095 .rhs = undefined, // translate and set afterward4083 .rhs = undefined, // translate and set afterward
4096 };4084 };
...@@ -4569,12 +4557,12 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour...@@ -4569,12 +4557,12 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
4569 .Pointer => {4557 .Pointer => {
4570 const child_qt = ZigClangType_getPointeeType(ty);4558 const child_qt = ZigClangType_getPointeeType(ty);
4571 if (qualTypeChildIsFnProto(child_qt)) {4559 if (qualTypeChildIsFnProto(child_qt)) {
4572 const optional_node = try transCreateNodePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");4560 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
4573 optional_node.rhs = try transQualType(rp, child_qt, source_loc);4561 optional_node.rhs = try transQualType(rp, child_qt, source_loc);
4574 return &optional_node.base;4562 return &optional_node.base;
4575 }4563 }
4576 if (typeIsOpaque(rp.c, ZigClangQualType_getTypePtr(child_qt), source_loc)) {4564 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, "?");
4578 const pointer_node = try transCreateNodePtrType(4566 const pointer_node = try transCreateNodePtrType(
4579 rp.c,4567 rp.c,
4580 ZigClangQualType_isConstQualified(child_qt),4568 ZigClangQualType_isConstQualified(child_qt),
...@@ -4599,21 +4587,8 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour...@@ -4599,21 +4587,8 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
45994587
4600 const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);4588 const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);
4601 const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));4589 const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));
4602 var node = try transCreateNodePrefixOp(4590 const elem_ty = ZigClangQualType_getTypePtr(ZigClangConstantArrayType_getElementType(const_arr_ty));
4603 rp.c,4591 return try transCreateNodeArrayType(rp, source_loc, elem_ty, size);
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;
4617 },4592 },
4618 .IncompleteArray => {4593 .IncompleteArray => {
4619 const incomplete_array_ty = @ptrCast(*const ZigClangIncompleteArrayType, ty);4594 const incomplete_array_ty = @ptrCast(*const ZigClangIncompleteArrayType, ty);
...@@ -5824,7 +5799,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5824,7 +5799,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5824 if (prev_id == .Keyword_void) {5799 if (prev_id == .Keyword_void) {
5825 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);5800 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
5826 ptr.rhs = node;5801 ptr.rhs = node;
5827 const optional_node = try transCreateNodePrefixOp(c, .OptionalType, .QuestionMark, "?");5802 const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?");
5828 optional_node.rhs = &ptr.base;5803 optional_node.rhs = &ptr.base;
5829 return &optional_node.base;5804 return &optional_node.base;
5830 } else {5805 } else {
...@@ -5993,18 +5968,18 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5993,18 +5968,18 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
59935968
5994 switch (op_tok.id) {5969 switch (op_tok.id) {
5995 .Bang => {5970 .Bang => {
5996 const node = try transCreateNodePrefixOp(c, .BoolNot, .Bang, "!");5971 const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
5997 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);5972 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
5998 return &node.base;5973 return &node.base;
5999 },5974 },
6000 .Minus => {5975 .Minus => {
6001 const node = try transCreateNodePrefixOp(c, .Negation, .Minus, "-");5976 const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
6002 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);5977 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6003 return &node.base;5978 return &node.base;
6004 },5979 },
6005 .Plus => return try parseCPrefixOpExpr(c, it, source, source_loc, scope),5980 .Plus => return try parseCPrefixOpExpr(c, it, source, source_loc, scope),
6006 .Tilde => {5981 .Tilde => {
6007 const node = try transCreateNodePrefixOp(c, .BitNot, .Tilde, "~");5982 const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
6008 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);5983 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6009 return &node.base;5984 return &node.base;
6010 },5985 },
...@@ -6013,7 +5988,7 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -6013,7 +5988,7 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
6013 return try transCreateNodePtrDeref(c, node);5988 return try transCreateNodePtrDeref(c, node);
6014 },5989 },
6015 .Ampersand => {5990 .Ampersand => {
6016 const node = try transCreateNodePrefixOp(c, .AddressOf, .Ampersand, "&");5991 const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
6017 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);5992 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6018 return &node.base;5993 return &node.base;
6019 },5994 },
...@@ -6034,29 +6009,49 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {...@@ -6034,29 +6009,49 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {
6034}6009}
60356010
6036fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {6011fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
6037 if (node.id == .ContainerDecl) {6012 switch (node.id) {
6038 return node;6013 .ContainerDecl,
6039 } else if (node.id == .PrefixOp) {6014 .AddressOf,
6040 return node;6015 .Await,
6041 } else if (node.cast(ast.Node.Identifier)) |ident| {6016 .BitNot,
6042 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {6017 .BoolNot,
6043 if (value.cast(ast.Node.VarDecl)) |var_decl|6018 .OptionalType,
6044 return getContainer(c, var_decl.init_node.?);6019 .Negation,
6045 }6020 .NegationWrap,
6046 } else if (node.cast(ast.Node.InfixOp)) |infix| {6021 .Resume,
6047 if (infix.op != .Period)6022 .Try,
6048 return null;6023 .ArrayType,
6049 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {6024 .ArrayTypeSentinel,
6050 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {6025 .PtrType,
6051 for (container.fieldsAndDecls()) |field_ref| {6026 .SliceType,
6052 const field = field_ref.cast(ast.Node.ContainerField).?;6027 => return node,
6053 const ident = infix.rhs.cast(ast.Node.Identifier).?;6028
6054 if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {6029 .Identifier => {
6055 return getContainer(c, field.type_expr.?);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 }
6056 }6049 }
6057 }6050 }
6058 }6051 }
6059 }6052 },
6053
6054 else => {},
6060 }6055 }
6061 return null;6056 return null;
6062}6057}
...@@ -6091,11 +6086,9 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {...@@ -6091,11 +6086,9 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
6091fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {6086fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
6092 const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null;6087 const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null;
6093 if (getContainerTypeOf(c, init)) |ty_node| {6088 if (getContainerTypeOf(c, init)) |ty_node| {
6094 if (ty_node.cast(ast.Node.PrefixOp)) |prefix| {6089 if (ty_node.cast(ast.Node.OptionalType)) |prefix| {
6095 if (prefix.op == .OptionalType) {6090 if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
6096 if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {6091 return fn_proto;
6097 return fn_proto;
6098 }
6099 }6092 }
6100 }6093 }
6101 }6094 }
src-self-hosted/type.zig+17-2
...@@ -163,6 +163,22 @@ pub const Type = extern union {...@@ -163,6 +163,22 @@ pub const Type = extern union {
163 return sentinel_b == null;163 return sentinel_b == null;
164 }164 }
165 },165 },
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 },
166 .Float,182 .Float,
167 .Struct,183 .Struct,
168 .Optional,184 .Optional,
...@@ -170,14 +186,13 @@ pub const Type = extern union {...@@ -170,14 +186,13 @@ pub const Type = extern union {
170 .ErrorSet,186 .ErrorSet,
171 .Enum,187 .Enum,
172 .Union,188 .Union,
173 .Fn,
174 .BoundFn,189 .BoundFn,
175 .Opaque,190 .Opaque,
176 .Frame,191 .Frame,
177 .AnyFrame,192 .AnyFrame,
178 .Vector,193 .Vector,
179 .EnumLiteral,194 .EnumLiteral,
180 => @panic("TODO implement more Type equality comparison"),195 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
181 }196 }
182 }197 }
183198
src-self-hosted/value.zig+23-11
...@@ -427,8 +427,6 @@ pub const Value = extern union {...@@ -427,8 +427,6 @@ pub const Value = extern union {
427 .fn_ccc_void_no_args_type,427 .fn_ccc_void_no_args_type,
428 .single_const_pointer_to_comptime_int_type,428 .single_const_pointer_to_comptime_int_type,
429 .const_slice_u8_type,429 .const_slice_u8_type,
430 .bool_true,
431 .bool_false,
432 .null_value,430 .null_value,
433 .function,431 .function,
434 .ref_val,432 .ref_val,
...@@ -441,8 +439,11 @@ pub const Value = extern union {...@@ -441,8 +439,11 @@ pub const Value = extern union {
441439
442 .the_one_possible_value, // An integer with one possible value is always zero.440 .the_one_possible_value, // An integer with one possible value is always zero.
443 .zero,441 .zero,
442 .bool_false,
444 => return BigIntMutable.init(&space.limbs, 0).toConst(),443 => return BigIntMutable.init(&space.limbs, 0).toConst(),
445444
445 .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(),
446
446 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),447 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
447 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),448 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
448 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),449 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
...@@ -493,8 +494,6 @@ pub const Value = extern union {...@@ -493,8 +494,6 @@ pub const Value = extern union {
493 .fn_ccc_void_no_args_type,494 .fn_ccc_void_no_args_type,
494 .single_const_pointer_to_comptime_int_type,495 .single_const_pointer_to_comptime_int_type,
495 .const_slice_u8_type,496 .const_slice_u8_type,
496 .bool_true,
497 .bool_false,
498 .null_value,497 .null_value,
499 .function,498 .function,
500 .ref_val,499 .ref_val,
...@@ -507,8 +506,11 @@ pub const Value = extern union {...@@ -507,8 +506,11 @@ pub const Value = extern union {
507506
508 .zero,507 .zero,
509 .the_one_possible_value, // an integer with one possible value is always zero508 .the_one_possible_value, // an integer with one possible value is always zero
509 .bool_false,
510 => return 0,510 => return 0,
511511
512 .bool_true => return 1,
513
512 .int_u64 => return self.cast(Payload.Int_u64).?.int,514 .int_u64 => return self.cast(Payload.Int_u64).?.int,
513 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),515 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
514 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,516 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
...@@ -560,8 +562,6 @@ pub const Value = extern union {...@@ -560,8 +562,6 @@ pub const Value = extern union {
560 .fn_ccc_void_no_args_type,562 .fn_ccc_void_no_args_type,
561 .single_const_pointer_to_comptime_int_type,563 .single_const_pointer_to_comptime_int_type,
562 .const_slice_u8_type,564 .const_slice_u8_type,
563 .bool_true,
564 .bool_false,
565 .null_value,565 .null_value,
566 .function,566 .function,
567 .ref_val,567 .ref_val,
...@@ -574,8 +574,11 @@ pub const Value = extern union {...@@ -574,8 +574,11 @@ pub const Value = extern union {
574574
575 .the_one_possible_value, // an integer with one possible value is always zero575 .the_one_possible_value, // an integer with one possible value is always zero
576 .zero,576 .zero,
577 .bool_false,
577 => return 0,578 => return 0,
578579
580 .bool_true => return 1,
581
579 .int_u64 => {582 .int_u64 => {
580 const x = self.cast(Payload.Int_u64).?.int;583 const x = self.cast(Payload.Int_u64).?.int;
581 if (x == 0) return 0;584 if (x == 0) return 0;
...@@ -632,8 +635,6 @@ pub const Value = extern union {...@@ -632,8 +635,6 @@ pub const Value = extern union {
632 .fn_ccc_void_no_args_type,635 .fn_ccc_void_no_args_type,
633 .single_const_pointer_to_comptime_int_type,636 .single_const_pointer_to_comptime_int_type,
634 .const_slice_u8_type,637 .const_slice_u8_type,
635 .bool_true,
636 .bool_false,
637 .null_value,638 .null_value,
638 .function,639 .function,
639 .ref_val,640 .ref_val,
...@@ -646,8 +647,18 @@ pub const Value = extern union {...@@ -646,8 +647,18 @@ pub const Value = extern union {
646 .zero,647 .zero,
647 .undef,648 .undef,
648 .the_one_possible_value, // an integer with one possible value is always zero649 .the_one_possible_value, // an integer with one possible value is always zero
650 .bool_false,
649 => return true,651 => 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
651 .int_u64 => switch (ty.zigTypeTag()) {662 .int_u64 => switch (ty.zigTypeTag()) {
652 .Int => {663 .Int => {
653 const x = self.cast(Payload.Int_u64).?.int;664 const x = self.cast(Payload.Int_u64).?.int;
...@@ -796,8 +807,6 @@ pub const Value = extern union {...@@ -796,8 +807,6 @@ pub const Value = extern union {
796 .fn_ccc_void_no_args_type,807 .fn_ccc_void_no_args_type,
797 .single_const_pointer_to_comptime_int_type,808 .single_const_pointer_to_comptime_int_type,
798 .const_slice_u8_type,809 .const_slice_u8_type,
799 .bool_true,
800 .bool_false,
801 .null_value,810 .null_value,
802 .function,811 .function,
803 .ref_val,812 .ref_val,
...@@ -810,8 +819,11 @@ pub const Value = extern union {...@@ -810,8 +819,11 @@ pub const Value = extern union {
810819
811 .zero,820 .zero,
812 .the_one_possible_value, // an integer with one possible value is always zero821 .the_one_possible_value, // an integer with one possible value is always zero
822 .bool_false,
813 => return .eq,823 => return .eq,
814824
825 .bool_true => return .gt,
826
815 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),827 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
816 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),828 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
817 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),829 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
...@@ -855,7 +867,7 @@ pub const Value = extern union {...@@ -855,7 +867,7 @@ pub const Value = extern union {
855 pub fn toBool(self: Value) bool {867 pub fn toBool(self: Value) bool {
856 return switch (self.tag()) {868 return switch (self.tag()) {
857 .bool_true => true,869 .bool_true => true,
858 .bool_false => false,870 .bool_false, .zero => false,
859 else => unreachable,871 else => unreachable,
860 };872 };
861 }873 }
src-self-hosted/zir.zig+30
...@@ -56,6 +56,7 @@ pub const Inst = struct {...@@ -56,6 +56,7 @@ pub const Inst = struct {
56 declval,56 declval,
57 /// Same as declval but the parameter is a `*Module.Decl` rather than a name.57 /// Same as declval but the parameter is a `*Module.Decl` rather than a name.
58 declval_in_module,58 declval_in_module,
59 boolnot,
59 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.60 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
60 str,61 str,
61 int,62 int,
...@@ -115,6 +116,7 @@ pub const Inst = struct {...@@ -115,6 +116,7 @@ pub const Inst = struct {
115 .cmp,116 .cmp,
116 .isnull,117 .isnull,
117 .isnonnull,118 .isnonnull,
119 .boolnot,
118 => false,120 => false,
119121
120 .condbr,122 .condbr,
...@@ -143,6 +145,7 @@ pub const Inst = struct {...@@ -143,6 +145,7 @@ pub const Inst = struct {
143 .declval_in_module => DeclValInModule,145 .declval_in_module => DeclValInModule,
144 .compileerror => CompileError,146 .compileerror => CompileError,
145 .@"const" => Const,147 .@"const" => Const,
148 .boolnot => BoolNot,
146 .str => Str,149 .str => Str,
147 .int => Int,150 .int => Int,
148 .inttype => IntType,151 .inttype => IntType,
...@@ -299,6 +302,16 @@ pub const Inst = struct {...@@ -299,6 +302,16 @@ pub const Inst = struct {
299 kw_args: struct {},302 kw_args: struct {},
300 };303 };
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
302 pub const Str = struct {315 pub const Str = struct {
303 pub const base_tag = Tag.str;316 pub const base_tag = Tag.str;
304 base: Inst,317 base: Inst,
...@@ -762,6 +775,7 @@ const Writer = struct {...@@ -762,6 +775,7 @@ const Writer = struct {
762 .declval_in_module => return self.writeInstToStreamGeneric(stream, .declval_in_module, inst),775 .declval_in_module => return self.writeInstToStreamGeneric(stream, .declval_in_module, inst),
763 .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, inst),776 .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, inst),
764 .@"const" => return self.writeInstToStreamGeneric(stream, .@"const", inst),777 .@"const" => return self.writeInstToStreamGeneric(stream, .@"const", inst),
778 .boolnot => return self.writeInstToStreamGeneric(stream, .boolnot, inst),
765 .str => return self.writeInstToStreamGeneric(stream, .str, inst),779 .str => return self.writeInstToStreamGeneric(stream, .str, inst),
766 .int => return self.writeInstToStreamGeneric(stream, .int, inst),780 .int => return self.writeInstToStreamGeneric(stream, .int, inst),
767 .inttype => return self.writeInstToStreamGeneric(stream, .inttype, inst),781 .inttype => return self.writeInstToStreamGeneric(stream, .inttype, inst),
...@@ -1658,6 +1672,22 @@ const EmitZIR = struct {...@@ -1658,6 +1672,22 @@ const EmitZIR = struct {
1658 };1672 };
1659 for (body.instructions) |inst| {1673 for (body.instructions) |inst| {
1660 const new_inst = switch (inst.tag) {1674 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 },
1661 .add => blk: {1691 .add => blk: {
1662 const old_inst = inst.cast(ir.Inst.Add).?;1692 const old_inst = inst.cast(ir.Inst.Add).?;
1663 const new_inst = try self.arena.allocator.create(Inst.Add);1693 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 {...@@ -170,4 +170,61 @@ pub fn addCases(ctx: *TestContext) !void {
170 "",170 "",
171 );171 );
172 }172 }
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 }
173}230}