| author | |
| committer | |
| log | 67273cbe7618253bffa56298b5bea0e1dd37dfc2 |
| tree | 7da05590dbc3b7f19a4cafdf1496a266bd4621a0 |
| parent | 03f14c3102ccd3e1811ccf4fb7cae11a75d3018f |
| parent | a92990f99312c946b5e527517a27a67a5a5513c0 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
Stage2 improvements12 files changed, 1010 insertions(+), 464 deletions(-)
lib/std/zig/ast.zig+169-71| ... | ... | @@ -410,7 +410,20 @@ pub const Node = struct { |
| 410 | 410 | |
| 411 | 411 | // Operators |
| 412 | 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 | 427 | /// Not all suffix operations are under this tag. To save memory, some |
| 415 | 428 | /// suffix operations have dedicated Node tags. |
| 416 | 429 | SuffixOp, |
| ... | ... | @@ -1797,85 +1810,116 @@ pub const Node = struct { |
| 1797 | 1810 | } |
| 1798 | 1811 | }; |
| 1799 | 1812 | |
| 1800 | pub const PrefixOp = struct { | |
| 1801 | base: Node = Node{ .id = .PrefixOp }, | |
| 1813 | pub const AddressOf = SimplePrefixOp(.AddressOf); | |
| 1814 | pub const Await = SimplePrefixOp(.Await); | |
| 1815 | pub const BitNot = SimplePrefixOp(.BitNot); | |
| 1816 | pub const BoolNot = SimplePrefixOp(.BoolNot); | |
| 1817 | pub const OptionalType = SimplePrefixOp(.OptionalType); | |
| 1818 | pub const Negation = SimplePrefixOp(.Negation); | |
| 1819 | pub const NegationWrap = SimplePrefixOp(.NegationWrap); | |
| 1820 | pub const Resume = SimplePrefixOp(.Resume); | |
| 1821 | pub const Try = SimplePrefixOp(.Try); | |
| 1822 | ||
| 1823 | pub fn SimplePrefixOp(comptime tag: Id) type { | |
| 1824 | return struct { | |
| 1825 | base: Node = Node{ .id = tag }, | |
| 1826 | op_token: TokenIndex, | |
| 1827 | rhs: *Node, | |
| 1828 | ||
| 1829 | const Self = @This(); | |
| 1830 | ||
| 1831 | pub fn iterate(self: *const Self, index: usize) ?*Node { | |
| 1832 | if (index == 0) return self.rhs; | |
| 1833 | return null; | |
| 1834 | } | |
| 1835 | ||
| 1836 | pub fn firstToken(self: *const Self) TokenIndex { | |
| 1837 | return self.op_token; | |
| 1838 | } | |
| 1839 | ||
| 1840 | pub fn lastToken(self: *const Self) TokenIndex { | |
| 1841 | return self.rhs.lastToken(); | |
| 1842 | } | |
| 1843 | }; | |
| 1844 | } | |
| 1845 | ||
| 1846 | pub const ArrayType = struct { | |
| 1847 | base: Node = Node{ .id = .ArrayType }, | |
| 1802 | 1848 | op_token: TokenIndex, |
| 1803 | op: Op, | |
| 1804 | 1849 | rhs: *Node, |
| 1850 | len_expr: *Node, | |
| 1805 | 1851 | |
| 1806 | pub const Op = union(enum) { | |
| 1807 | AddressOf, | |
| 1808 | ArrayType: ArrayInfo, | |
| 1809 | Await, | |
| 1810 | BitNot, | |
| 1811 | BoolNot, | |
| 1812 | OptionalType, | |
| 1813 | Negation, | |
| 1814 | NegationWrap, | |
| 1815 | Resume, | |
| 1816 | PtrType: PtrInfo, | |
| 1817 | SliceType: PtrInfo, | |
| 1818 | Try, | |
| 1819 | }; | |
| 1852 | pub fn iterate(self: *const ArrayType, index: usize) ?*Node { | |
| 1853 | var i = index; | |
| 1820 | 1854 | |
| 1821 | pub const ArrayInfo = struct { | |
| 1822 | len_expr: *Node, | |
| 1823 | sentinel: ?*Node, | |
| 1824 | }; | |
| 1855 | if (i < 1) return self.len_expr; | |
| 1856 | i -= 1; | |
| 1825 | 1857 | |
| 1826 | pub const PtrInfo = struct { | |
| 1827 | allowzero_token: ?TokenIndex = null, | |
| 1828 | align_info: ?Align = null, | |
| 1829 | const_token: ?TokenIndex = null, | |
| 1830 | volatile_token: ?TokenIndex = null, | |
| 1831 | sentinel: ?*Node = null, | |
| 1832 | ||
| 1833 | pub const Align = struct { | |
| 1834 | node: *Node, | |
| 1835 | bit_range: ?BitRange, | |
| 1836 | ||
| 1837 | pub const BitRange = struct { | |
| 1838 | start: *Node, | |
| 1839 | end: *Node, | |
| 1840 | }; | |
| 1841 | }; | |
| 1842 | }; | |
| 1858 | if (i < 1) return self.rhs; | |
| 1859 | i -= 1; | |
| 1860 | ||
| 1861 | return null; | |
| 1862 | } | |
| 1843 | 1863 | |
| 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 | 1881 | var i = index; |
| 1846 | 1882 | |
| 1847 | switch (self.op) { | |
| 1848 | .PtrType, .SliceType => |addr_of_info| { | |
| 1849 | if (addr_of_info.sentinel) |sentinel| { | |
| 1850 | if (i < 1) return sentinel; | |
| 1851 | i -= 1; | |
| 1852 | } | |
| 1883 | if (i < 1) return self.len_expr; | |
| 1884 | i -= 1; | |
| 1853 | 1885 | |
| 1854 | if (addr_of_info.align_info) |align_info| { | |
| 1855 | if (i < 1) return align_info.node; | |
| 1856 | i -= 1; | |
| 1857 | } | |
| 1858 | }, | |
| 1886 | if (i < 1) return self.sentinel; | |
| 1887 | i -= 1; | |
| 1859 | 1888 | |
| 1860 | .ArrayType => |array_info| { | |
| 1861 | if (i < 1) return array_info.len_expr; | |
| 1862 | i -= 1; | |
| 1863 | if (array_info.sentinel) |sentinel| { | |
| 1864 | if (i < 1) return sentinel; | |
| 1865 | i -= 1; | |
| 1866 | } | |
| 1867 | }, | |
| 1889 | if (i < 1) return self.rhs; | |
| 1890 | i -= 1; | |
| 1868 | 1891 | |
| 1869 | .AddressOf, | |
| 1870 | .Await, | |
| 1871 | .BitNot, | |
| 1872 | .BoolNot, | |
| 1873 | .OptionalType, | |
| 1874 | .Negation, | |
| 1875 | .NegationWrap, | |
| 1876 | .Try, | |
| 1877 | .Resume, | |
| 1878 | => {}, | |
| 1892 | return null; | |
| 1893 | } | |
| 1894 | ||
| 1895 | pub fn firstToken(self: *const ArrayTypeSentinel) TokenIndex { | |
| 1896 | return self.op_token; | |
| 1897 | } | |
| 1898 | ||
| 1899 | pub fn lastToken(self: *const ArrayTypeSentinel) TokenIndex { | |
| 1900 | return self.rhs.lastToken(); | |
| 1901 | } | |
| 1902 | }; | |
| 1903 | ||
| 1904 | pub const PtrType = struct { | |
| 1905 | base: Node = Node{ .id = .PtrType }, | |
| 1906 | op_token: TokenIndex, | |
| 1907 | rhs: *Node, | |
| 1908 | /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents | |
| 1909 | /// one of these possibly-null things. Then we have them directly follow the PtrType in memory. | |
| 1910 | ptr_info: PtrInfo = .{}, | |
| 1911 | ||
| 1912 | pub fn iterate(self: *const PtrType, index: usize) ?*Node { | |
| 1913 | var i = index; | |
| 1914 | ||
| 1915 | if (self.ptr_info.sentinel) |sentinel| { | |
| 1916 | if (i < 1) return sentinel; | |
| 1917 | i -= 1; | |
| 1918 | } | |
| 1919 | ||
| 1920 | if (self.ptr_info.align_info) |align_info| { | |
| 1921 | if (i < 1) return align_info.node; | |
| 1922 | i -= 1; | |
| 1879 | 1923 | } |
| 1880 | 1924 | |
| 1881 | 1925 | if (i < 1) return self.rhs; |
| ... | ... | @@ -1884,11 +1928,47 @@ pub const Node = struct { |
| 1884 | 1928 | return null; |
| 1885 | 1929 | } |
| 1886 | 1930 | |
| 1887 | pub fn firstToken(self: *const PrefixOp) TokenIndex { | |
| 1931 | pub fn firstToken(self: *const PtrType) TokenIndex { | |
| 1888 | 1932 | return self.op_token; |
| 1889 | 1933 | } |
| 1890 | 1934 | |
| 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 | 1972 | return self.rhs.lastToken(); |
| 1893 | 1973 | } |
| 1894 | 1974 | }; |
| ... | ... | @@ -2797,6 +2877,24 @@ pub const Node = struct { |
| 2797 | 2877 | }; |
| 2798 | 2878 | }; |
| 2799 | 2879 | |
| 2880 | pub 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 | ||
| 2800 | 2898 | test "iterate" { |
| 2801 | 2899 | var root = Node.Root{ |
| 2802 | 2900 | .base = Node{ .id = Node.Id.Root }, |
lib/std/zig/parse.zig+243-105| ... | ... | @@ -1120,10 +1120,9 @@ const Parser = struct { |
| 1120 | 1120 | const expr_node = try p.expectNode(parseExpr, .{ |
| 1121 | 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 | 1124 | node.* = .{ |
| 1125 | 1125 | .op_token = token, |
| 1126 | .op = .Resume, | |
| 1127 | 1126 | .rhs = expr_node, |
| 1128 | 1127 | }; |
| 1129 | 1128 | return &node.base; |
| ... | ... | @@ -2413,24 +2412,25 @@ const Parser = struct { |
| 2413 | 2412 | /// / KEYWORD_await |
| 2414 | 2413 | fn parsePrefixOp(p: *Parser) !?*Node { |
| 2415 | 2414 | const token = p.nextToken(); |
| 2416 | const op: Node.PrefixOp.Op = switch (p.token_ids[token]) { | |
| 2417 | .Bang => .BoolNot, | |
| 2418 | .Minus => .Negation, | |
| 2419 | .Tilde => .BitNot, | |
| 2420 | .MinusPercent => .NegationWrap, | |
| 2421 | .Ampersand => .AddressOf, | |
| 2422 | .Keyword_try => .Try, | |
| 2423 | .Keyword_await => .Await, | |
| 2415 | switch (p.token_ids[token]) { | |
| 2416 | .Bang => return p.allocSimplePrefixOp(.BoolNot, token), | |
| 2417 | .Minus => return p.allocSimplePrefixOp(.Negation, token), | |
| 2418 | .Tilde => return p.allocSimplePrefixOp(.BitNot, token), | |
| 2419 | .MinusPercent => return p.allocSimplePrefixOp(.NegationWrap, token), | |
| 2420 | .Ampersand => return p.allocSimplePrefixOp(.AddressOf, token), | |
| 2421 | .Keyword_try => return p.allocSimplePrefixOp(.Try, token), | |
| 2422 | .Keyword_await => return p.allocSimplePrefixOp(.Await, token), | |
| 2424 | 2423 | else => { |
| 2425 | 2424 | p.putBackToken(token); |
| 2426 | 2425 | return null; |
| 2427 | 2426 | }, |
| 2428 | }; | |
| 2427 | } | |
| 2428 | } | |
| 2429 | 2429 | |
| 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 | 2432 | node.* = .{ |
| 2432 | 2433 | .op_token = token, |
| 2433 | .op = op, | |
| 2434 | 2434 | .rhs = undefined, // set by caller |
| 2435 | 2435 | }; |
| 2436 | 2436 | return &node.base; |
| ... | ... | @@ -2450,19 +2450,14 @@ const Parser = struct { |
| 2450 | 2450 | /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* |
| 2451 | 2451 | fn parsePrefixTypeOp(p: *Parser) !?*Node { |
| 2452 | 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 | 2454 | node.* = .{ |
| 2455 | 2455 | .op_token = token, |
| 2456 | .op = .OptionalType, | |
| 2457 | 2456 | .rhs = undefined, // set by caller |
| 2458 | 2457 | }; |
| 2459 | 2458 | return &node.base; |
| 2460 | 2459 | } |
| 2461 | 2460 | |
| 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 | 2461 | if (p.eatToken(.Keyword_anyframe)) |token| { |
| 2467 | 2462 | const arrow = p.eatToken(.Arrow) orelse { |
| 2468 | 2463 | p.putBackToken(token); |
| ... | ... | @@ -2482,11 +2477,15 @@ const Parser = struct { |
| 2482 | 2477 | if (try p.parsePtrTypeStart()) |node| { |
| 2483 | 2478 | // If the token encountered was **, there will be two nodes instead of one. |
| 2484 | 2479 | // The attributes should be applied to the rightmost operator. |
| 2485 | const prefix_op = node.cast(Node.PrefixOp).?; | |
| 2486 | var ptr_info = if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk) | |
| 2487 | &prefix_op.rhs.cast(Node.PrefixOp).?.op.PtrType | |
| 2480 | var ptr_info = if (node.cast(Node.PtrType)) |ptr_type| | |
| 2481 | if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) | |
| 2482 | &ptr_type.rhs.cast(Node.PtrType).?.ptr_info | |
| 2483 | else | |
| 2484 | &ptr_type.ptr_info | |
| 2485 | else if (node.cast(Node.SliceType)) |slice_type| | |
| 2486 | &slice_type.ptr_info | |
| 2488 | 2487 | else |
| 2489 | &prefix_op.op.PtrType; | |
| 2488 | unreachable; | |
| 2490 | 2489 | |
| 2491 | 2490 | while (true) { |
| 2492 | 2491 | if (p.eatToken(.Keyword_align)) |align_token| { |
| ... | ... | @@ -2505,7 +2504,7 @@ const Parser = struct { |
| 2505 | 2504 | .ExpectedIntegerLiteral = .{ .token = p.tok_i }, |
| 2506 | 2505 | }); |
| 2507 | 2506 | |
| 2508 | break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{ | |
| 2507 | break :bit_range_value ast.PtrInfo.Align.BitRange{ | |
| 2509 | 2508 | .start = range_start, |
| 2510 | 2509 | .end = range_end, |
| 2511 | 2510 | }; |
| ... | ... | @@ -2519,7 +2518,7 @@ const Parser = struct { |
| 2519 | 2518 | continue; |
| 2520 | 2519 | } |
| 2521 | 2520 | |
| 2522 | ptr_info.align_info = Node.PrefixOp.PtrInfo.Align{ | |
| 2521 | ptr_info.align_info = ast.PtrInfo.Align{ | |
| 2523 | 2522 | .node = expr_node, |
| 2524 | 2523 | .bit_range = bit_range, |
| 2525 | 2524 | }; |
| ... | ... | @@ -2563,58 +2562,54 @@ const Parser = struct { |
| 2563 | 2562 | } |
| 2564 | 2563 | |
| 2565 | 2564 | if (try p.parseArrayTypeStart()) |node| { |
| 2566 | switch (node.cast(Node.PrefixOp).?.op) { | |
| 2567 | .ArrayType => {}, | |
| 2568 | .SliceType => |*slice_type| { | |
| 2569 | // Collect pointer qualifiers in any order, but disallow duplicates | |
| 2570 | while (true) { | |
| 2571 | if (try p.parseByteAlign()) |align_expr| { | |
| 2572 | if (slice_type.align_info != null) { | |
| 2573 | try p.errors.append(p.gpa, .{ | |
| 2574 | .ExtraAlignQualifier = .{ .token = p.tok_i - 1 }, | |
| 2575 | }); | |
| 2576 | continue; | |
| 2577 | } | |
| 2578 | slice_type.align_info = Node.PrefixOp.PtrInfo.Align{ | |
| 2579 | .node = align_expr, | |
| 2580 | .bit_range = null, | |
| 2581 | }; | |
| 2565 | if (node.cast(Node.SliceType)) |slice_type| { | |
| 2566 | // Collect pointer qualifiers in any order, but disallow duplicates | |
| 2567 | while (true) { | |
| 2568 | if (try p.parseByteAlign()) |align_expr| { | |
| 2569 | if (slice_type.ptr_info.align_info != null) { | |
| 2570 | try p.errors.append(p.gpa, .{ | |
| 2571 | .ExtraAlignQualifier = .{ .token = p.tok_i - 1 }, | |
| 2572 | }); | |
| 2582 | 2573 | continue; |
| 2583 | 2574 | } |
| 2584 | if (p.eatToken(.Keyword_const)) |const_token| { | |
| 2585 | if (slice_type.const_token != null) { | |
| 2586 | try p.errors.append(p.gpa, .{ | |
| 2587 | .ExtraConstQualifier = .{ .token = p.tok_i - 1 }, | |
| 2588 | }); | |
| 2589 | continue; | |
| 2590 | } | |
| 2591 | slice_type.const_token = const_token; | |
| 2575 | slice_type.ptr_info.align_info = ast.PtrInfo.Align{ | |
| 2576 | .node = align_expr, | |
| 2577 | .bit_range = null, | |
| 2578 | }; | |
| 2579 | continue; | |
| 2580 | } | |
| 2581 | if (p.eatToken(.Keyword_const)) |const_token| { | |
| 2582 | if (slice_type.ptr_info.const_token != null) { | |
| 2583 | try p.errors.append(p.gpa, .{ | |
| 2584 | .ExtraConstQualifier = .{ .token = p.tok_i - 1 }, | |
| 2585 | }); | |
| 2592 | 2586 | continue; |
| 2593 | 2587 | } |
| 2594 | if (p.eatToken(.Keyword_volatile)) |volatile_token| { | |
| 2595 | if (slice_type.volatile_token != null) { | |
| 2596 | try p.errors.append(p.gpa, .{ | |
| 2597 | .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 }, | |
| 2598 | }); | |
| 2599 | continue; | |
| 2600 | } | |
| 2601 | slice_type.volatile_token = volatile_token; | |
| 2588 | slice_type.ptr_info.const_token = const_token; | |
| 2589 | continue; | |
| 2590 | } | |
| 2591 | if (p.eatToken(.Keyword_volatile)) |volatile_token| { | |
| 2592 | if (slice_type.ptr_info.volatile_token != null) { | |
| 2593 | try p.errors.append(p.gpa, .{ | |
| 2594 | .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 }, | |
| 2595 | }); | |
| 2602 | 2596 | continue; |
| 2603 | 2597 | } |
| 2604 | if (p.eatToken(.Keyword_allowzero)) |allowzero_token| { | |
| 2605 | if (slice_type.allowzero_token != null) { | |
| 2606 | try p.errors.append(p.gpa, .{ | |
| 2607 | .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 }, | |
| 2608 | }); | |
| 2609 | continue; | |
| 2610 | } | |
| 2611 | slice_type.allowzero_token = allowzero_token; | |
| 2598 | slice_type.ptr_info.volatile_token = volatile_token; | |
| 2599 | continue; | |
| 2600 | } | |
| 2601 | if (p.eatToken(.Keyword_allowzero)) |allowzero_token| { | |
| 2602 | if (slice_type.ptr_info.allowzero_token != null) { | |
| 2603 | try p.errors.append(p.gpa, .{ | |
| 2604 | .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 }, | |
| 2605 | }); | |
| 2612 | 2606 | continue; |
| 2613 | 2607 | } |
| 2614 | break; | |
| 2608 | slice_type.ptr_info.allowzero_token = allowzero_token; | |
| 2609 | continue; | |
| 2615 | 2610 | } |
| 2616 | }, | |
| 2617 | else => unreachable, | |
| 2611 | break; | |
| 2612 | } | |
| 2618 | 2613 | } |
| 2619 | 2614 | return node; |
| 2620 | 2615 | } |
| ... | ... | @@ -2728,29 +2723,32 @@ const Parser = struct { |
| 2728 | 2723 | null; |
| 2729 | 2724 | const rbracket = try p.expectToken(.RBracket); |
| 2730 | 2725 | |
| 2731 | const op: Node.PrefixOp.Op = if (expr) |len_expr| | |
| 2732 | .{ | |
| 2733 | .ArrayType = .{ | |
| 2726 | if (expr) |len_expr| { | |
| 2727 | if (sentinel) |s| { | |
| 2728 | const node = try p.arena.allocator.create(Node.ArrayTypeSentinel); | |
| 2729 | node.* = .{ | |
| 2730 | .op_token = lbracket, | |
| 2731 | .rhs = undefined, // set by caller | |
| 2734 | 2732 | .len_expr = len_expr, |
| 2735 | .sentinel = sentinel, | |
| 2736 | }, | |
| 2733 | .sentinel = s, | |
| 2734 | }; | |
| 2735 | return &node.base; | |
| 2736 | } else { | |
| 2737 | const node = try p.arena.allocator.create(Node.ArrayType); | |
| 2738 | node.* = .{ | |
| 2739 | .op_token = lbracket, | |
| 2740 | .rhs = undefined, // set by caller | |
| 2741 | .len_expr = len_expr, | |
| 2742 | }; | |
| 2743 | return &node.base; | |
| 2737 | 2744 | } |
| 2738 | else | |
| 2739 | .{ | |
| 2740 | .SliceType = Node.PrefixOp.PtrInfo{ | |
| 2741 | .allowzero_token = null, | |
| 2742 | .align_info = null, | |
| 2743 | .const_token = null, | |
| 2744 | .volatile_token = null, | |
| 2745 | .sentinel = sentinel, | |
| 2746 | }, | |
| 2747 | }; | |
| 2745 | } | |
| 2748 | 2746 | |
| 2749 | const node = try p.arena.allocator.create(Node.PrefixOp); | |
| 2747 | const node = try p.arena.allocator.create(Node.SliceType); | |
| 2750 | 2748 | node.* = .{ |
| 2751 | 2749 | .op_token = lbracket, |
| 2752 | .op = op, | |
| 2753 | 2750 | .rhs = undefined, // set by caller |
| 2751 | .ptr_info = .{ .sentinel = sentinel }, | |
| 2754 | 2752 | }; |
| 2755 | 2753 | return &node.base; |
| 2756 | 2754 | } |
| ... | ... | @@ -2768,28 +2766,26 @@ const Parser = struct { |
| 2768 | 2766 | }) |
| 2769 | 2767 | else |
| 2770 | 2768 | null; |
| 2771 | const node = try p.arena.allocator.create(Node.PrefixOp); | |
| 2769 | const node = try p.arena.allocator.create(Node.PtrType); | |
| 2772 | 2770 | node.* = .{ |
| 2773 | 2771 | .op_token = asterisk, |
| 2774 | .op = .{ .PtrType = .{ .sentinel = sentinel } }, | |
| 2775 | 2772 | .rhs = undefined, // set by caller |
| 2773 | .ptr_info = .{ .sentinel = sentinel }, | |
| 2776 | 2774 | }; |
| 2777 | 2775 | return &node.base; |
| 2778 | 2776 | } |
| 2779 | 2777 | |
| 2780 | 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 | 2780 | node.* = .{ |
| 2783 | 2781 | .op_token = double_asterisk, |
| 2784 | .op = .{ .PtrType = .{} }, | |
| 2785 | 2782 | .rhs = undefined, // set by caller |
| 2786 | 2783 | }; |
| 2787 | 2784 | |
| 2788 | 2785 | // 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 | 2787 | child.* = .{ |
| 2791 | 2788 | .op_token = double_asterisk, |
| 2792 | .op = .{ .PtrType = .{} }, | |
| 2793 | 2789 | .rhs = undefined, // set by caller |
| 2794 | 2790 | }; |
| 2795 | 2791 | node.rhs = &child.base; |
| ... | ... | @@ -2808,10 +2804,9 @@ const Parser = struct { |
| 2808 | 2804 | p.putBackToken(ident); |
| 2809 | 2805 | } else { |
| 2810 | 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 | 2808 | node.* = .{ |
| 2813 | 2809 | .op_token = lbracket, |
| 2814 | .op = .{ .PtrType = .{} }, | |
| 2815 | 2810 | .rhs = undefined, // set by caller |
| 2816 | 2811 | }; |
| 2817 | 2812 | return &node.base; |
| ... | ... | @@ -2824,11 +2819,11 @@ const Parser = struct { |
| 2824 | 2819 | else |
| 2825 | 2820 | null; |
| 2826 | 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 | 2823 | node.* = .{ |
| 2829 | 2824 | .op_token = lbracket, |
| 2830 | .op = .{ .PtrType = .{ .sentinel = sentinel } }, | |
| 2831 | 2825 | .rhs = undefined, // set by caller |
| 2826 | .ptr_info = .{ .sentinel = sentinel }, | |
| 2832 | 2827 | }; |
| 2833 | 2828 | return &node.base; |
| 2834 | 2829 | } |
| ... | ... | @@ -3146,10 +3141,9 @@ const Parser = struct { |
| 3146 | 3141 | |
| 3147 | 3142 | fn parseTry(p: *Parser) !?*Node { |
| 3148 | 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 | 3145 | node.* = .{ |
| 3151 | 3146 | .op_token = token, |
| 3152 | .op = .Try, | |
| 3153 | 3147 | .rhs = undefined, // set by caller |
| 3154 | 3148 | }; |
| 3155 | 3149 | return &node.base; |
| ... | ... | @@ -3228,15 +3222,87 @@ const Parser = struct { |
| 3228 | 3222 | var rightmost_op = first_op; |
| 3229 | 3223 | while (true) { |
| 3230 | 3224 | switch (rightmost_op.id) { |
| 3231 | .PrefixOp => { | |
| 3232 | var prefix_op = rightmost_op.cast(Node.PrefixOp).?; | |
| 3225 | .AddressOf => { | |
| 3226 | if (try opParseFn(p)) |rhs| { | |
| 3227 | rightmost_op.cast(Node.AddressOf).?.rhs = rhs; | |
| 3228 | rightmost_op = rhs; | |
| 3229 | } else break; | |
| 3230 | }, | |
| 3231 | .Await => { | |
| 3232 | if (try opParseFn(p)) |rhs| { | |
| 3233 | rightmost_op.cast(Node.Await).?.rhs = rhs; | |
| 3234 | rightmost_op = rhs; | |
| 3235 | } else break; | |
| 3236 | }, | |
| 3237 | .BitNot => { | |
| 3238 | if (try opParseFn(p)) |rhs| { | |
| 3239 | rightmost_op.cast(Node.BitNot).?.rhs = rhs; | |
| 3240 | rightmost_op = rhs; | |
| 3241 | } else break; | |
| 3242 | }, | |
| 3243 | .BoolNot => { | |
| 3244 | if (try opParseFn(p)) |rhs| { | |
| 3245 | rightmost_op.cast(Node.BoolNot).?.rhs = rhs; | |
| 3246 | rightmost_op = rhs; | |
| 3247 | } else break; | |
| 3248 | }, | |
| 3249 | .OptionalType => { | |
| 3250 | if (try opParseFn(p)) |rhs| { | |
| 3251 | rightmost_op.cast(Node.OptionalType).?.rhs = rhs; | |
| 3252 | rightmost_op = rhs; | |
| 3253 | } else break; | |
| 3254 | }, | |
| 3255 | .Negation => { | |
| 3256 | if (try opParseFn(p)) |rhs| { | |
| 3257 | rightmost_op.cast(Node.Negation).?.rhs = rhs; | |
| 3258 | rightmost_op = rhs; | |
| 3259 | } else break; | |
| 3260 | }, | |
| 3261 | .NegationWrap => { | |
| 3262 | if (try opParseFn(p)) |rhs| { | |
| 3263 | rightmost_op.cast(Node.NegationWrap).?.rhs = rhs; | |
| 3264 | rightmost_op = rhs; | |
| 3265 | } else break; | |
| 3266 | }, | |
| 3267 | .Resume => { | |
| 3268 | if (try opParseFn(p)) |rhs| { | |
| 3269 | rightmost_op.cast(Node.Resume).?.rhs = rhs; | |
| 3270 | rightmost_op = rhs; | |
| 3271 | } else break; | |
| 3272 | }, | |
| 3273 | .Try => { | |
| 3274 | if (try opParseFn(p)) |rhs| { | |
| 3275 | rightmost_op.cast(Node.Try).?.rhs = rhs; | |
| 3276 | rightmost_op = rhs; | |
| 3277 | } else break; | |
| 3278 | }, | |
| 3279 | .ArrayType => { | |
| 3280 | if (try opParseFn(p)) |rhs| { | |
| 3281 | rightmost_op.cast(Node.ArrayType).?.rhs = rhs; | |
| 3282 | rightmost_op = rhs; | |
| 3283 | } else break; | |
| 3284 | }, | |
| 3285 | .ArrayTypeSentinel => { | |
| 3286 | if (try opParseFn(p)) |rhs| { | |
| 3287 | rightmost_op.cast(Node.ArrayTypeSentinel).?.rhs = rhs; | |
| 3288 | rightmost_op = rhs; | |
| 3289 | } else break; | |
| 3290 | }, | |
| 3291 | .SliceType => { | |
| 3292 | if (try opParseFn(p)) |rhs| { | |
| 3293 | rightmost_op.cast(Node.SliceType).?.rhs = rhs; | |
| 3294 | rightmost_op = rhs; | |
| 3295 | } else break; | |
| 3296 | }, | |
| 3297 | .PtrType => { | |
| 3298 | var ptr_type = rightmost_op.cast(Node.PtrType).?; | |
| 3233 | 3299 | // If the token encountered was **, there will be two nodes |
| 3234 | if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk) { | |
| 3235 | rightmost_op = prefix_op.rhs; | |
| 3236 | prefix_op = rightmost_op.cast(Node.PrefixOp).?; | |
| 3300 | if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) { | |
| 3301 | rightmost_op = ptr_type.rhs; | |
| 3302 | ptr_type = rightmost_op.cast(Node.PtrType).?; | |
| 3237 | 3303 | } |
| 3238 | 3304 | if (try opParseFn(p)) |rhs| { |
| 3239 | prefix_op.rhs = rhs; | |
| 3305 | ptr_type.rhs = rhs; | |
| 3240 | 3306 | rightmost_op = rhs; |
| 3241 | 3307 | } else break; |
| 3242 | 3308 | }, |
| ... | ... | @@ -3253,8 +3319,80 @@ const Parser = struct { |
| 3253 | 3319 | |
| 3254 | 3320 | // If any prefix op existed, a child node on the RHS is required |
| 3255 | 3321 | switch (rightmost_op.id) { |
| 3256 | .PrefixOp => { | |
| 3257 | const prefix_op = rightmost_op.cast(Node.PrefixOp).?; | |
| 3322 | .AddressOf => { | |
| 3323 | const prefix_op = rightmost_op.cast(Node.AddressOf).?; | |
| 3324 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3325 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3326 | }); | |
| 3327 | }, | |
| 3328 | .Await => { | |
| 3329 | const prefix_op = rightmost_op.cast(Node.Await).?; | |
| 3330 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3331 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3332 | }); | |
| 3333 | }, | |
| 3334 | .BitNot => { | |
| 3335 | const prefix_op = rightmost_op.cast(Node.BitNot).?; | |
| 3336 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3337 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3338 | }); | |
| 3339 | }, | |
| 3340 | .BoolNot => { | |
| 3341 | const prefix_op = rightmost_op.cast(Node.BoolNot).?; | |
| 3342 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3343 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3344 | }); | |
| 3345 | }, | |
| 3346 | .OptionalType => { | |
| 3347 | const prefix_op = rightmost_op.cast(Node.OptionalType).?; | |
| 3348 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3349 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3350 | }); | |
| 3351 | }, | |
| 3352 | .Negation => { | |
| 3353 | const prefix_op = rightmost_op.cast(Node.Negation).?; | |
| 3354 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3355 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3356 | }); | |
| 3357 | }, | |
| 3358 | .NegationWrap => { | |
| 3359 | const prefix_op = rightmost_op.cast(Node.NegationWrap).?; | |
| 3360 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3361 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3362 | }); | |
| 3363 | }, | |
| 3364 | .Resume => { | |
| 3365 | const prefix_op = rightmost_op.cast(Node.Resume).?; | |
| 3366 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3367 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3368 | }); | |
| 3369 | }, | |
| 3370 | .Try => { | |
| 3371 | const prefix_op = rightmost_op.cast(Node.Try).?; | |
| 3372 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3373 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3374 | }); | |
| 3375 | }, | |
| 3376 | .ArrayType => { | |
| 3377 | const prefix_op = rightmost_op.cast(Node.ArrayType).?; | |
| 3378 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3379 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3380 | }); | |
| 3381 | }, | |
| 3382 | .ArrayTypeSentinel => { | |
| 3383 | const prefix_op = rightmost_op.cast(Node.ArrayTypeSentinel).?; | |
| 3384 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3385 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3386 | }); | |
| 3387 | }, | |
| 3388 | .PtrType => { | |
| 3389 | const prefix_op = rightmost_op.cast(Node.PtrType).?; | |
| 3390 | prefix_op.rhs = try p.expectNode(childParseFn, .{ | |
| 3391 | .InvalidToken = .{ .token = p.tok_i }, | |
| 3392 | }); | |
| 3393 | }, | |
| 3394 | .SliceType => { | |
| 3395 | const prefix_op = rightmost_op.cast(Node.SliceType).?; | |
| 3258 | 3396 | prefix_op.rhs = try p.expectNode(childParseFn, .{ |
| 3259 | 3397 | .InvalidToken = .{ .token = p.tok_i }, |
| 3260 | 3398 | }); |
lib/std/zig/render.zig+211-145| ... | ... | @@ -468,166 +468,192 @@ fn renderExpression( |
| 468 | 468 | return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space); |
| 469 | 469 | }, |
| 470 | 470 | |
| 471 | .PrefixOp => { | |
| 472 | const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); | |
| 473 | ||
| 474 | switch (prefix_op_node.op) { | |
| 475 | .PtrType => |ptr_info| { | |
| 476 | const op_tok_id = tree.token_ids[prefix_op_node.op_token]; | |
| 477 | switch (op_tok_id) { | |
| 478 | .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'), | |
| 479 | .LBracket => if (tree.token_ids[prefix_op_node.op_token + 2] == .Identifier) | |
| 480 | try stream.writeAll("[*c") | |
| 481 | else | |
| 482 | try stream.writeAll("[*"), | |
| 483 | else => unreachable, | |
| 484 | } | |
| 485 | if (ptr_info.sentinel) |sentinel| { | |
| 486 | const colon_token = tree.prevToken(sentinel.firstToken()); | |
| 487 | try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : | |
| 488 | const sentinel_space = switch (op_tok_id) { | |
| 489 | .LBracket => Space.None, | |
| 490 | else => Space.Space, | |
| 491 | }; | |
| 492 | try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space); | |
| 493 | } | |
| 494 | switch (op_tok_id) { | |
| 495 | .Asterisk, .AsteriskAsterisk => {}, | |
| 496 | .LBracket => try stream.writeByte(']'), | |
| 497 | else => unreachable, | |
| 498 | } | |
| 499 | if (ptr_info.allowzero_token) |allowzero_token| { | |
| 500 | try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero | |
| 501 | } | |
| 502 | if (ptr_info.align_info) |align_info| { | |
| 503 | const lparen_token = tree.prevToken(align_info.node.firstToken()); | |
| 504 | const align_token = tree.prevToken(lparen_token); | |
| 505 | ||
| 506 | try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align | |
| 507 | try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( | |
| 508 | ||
| 509 | try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None); | |
| 510 | ||
| 511 | if (align_info.bit_range) |bit_range| { | |
| 512 | const colon1 = tree.prevToken(bit_range.start.firstToken()); | |
| 513 | const colon2 = tree.prevToken(bit_range.end.firstToken()); | |
| 471 | .BitNot => { | |
| 472 | const bit_not = @fieldParentPtr(ast.Node.BitNot, "base", base); | |
| 473 | try renderToken(tree, stream, bit_not.op_token, indent, start_col, Space.None); | |
| 474 | return renderExpression(allocator, stream, tree, indent, start_col, bit_not.rhs, space); | |
| 475 | }, | |
| 476 | .BoolNot => { | |
| 477 | const bool_not = @fieldParentPtr(ast.Node.BoolNot, "base", base); | |
| 478 | try renderToken(tree, stream, bool_not.op_token, indent, start_col, Space.None); | |
| 479 | return renderExpression(allocator, stream, tree, indent, start_col, bool_not.rhs, space); | |
| 480 | }, | |
| 481 | .Negation => { | |
| 482 | const negation = @fieldParentPtr(ast.Node.Negation, "base", base); | |
| 483 | try renderToken(tree, stream, negation.op_token, indent, start_col, Space.None); | |
| 484 | return renderExpression(allocator, stream, tree, indent, start_col, negation.rhs, space); | |
| 485 | }, | |
| 486 | .NegationWrap => { | |
| 487 | const negation_wrap = @fieldParentPtr(ast.Node.NegationWrap, "base", base); | |
| 488 | try renderToken(tree, stream, negation_wrap.op_token, indent, start_col, Space.None); | |
| 489 | return renderExpression(allocator, stream, tree, indent, start_col, negation_wrap.rhs, space); | |
| 490 | }, | |
| 491 | .OptionalType => { | |
| 492 | const opt_type = @fieldParentPtr(ast.Node.OptionalType, "base", base); | |
| 493 | try renderToken(tree, stream, opt_type.op_token, indent, start_col, Space.None); | |
| 494 | return renderExpression(allocator, stream, tree, indent, start_col, opt_type.rhs, space); | |
| 495 | }, | |
| 496 | .AddressOf => { | |
| 497 | const addr_of = @fieldParentPtr(ast.Node.AddressOf, "base", base); | |
| 498 | try renderToken(tree, stream, addr_of.op_token, indent, start_col, Space.None); | |
| 499 | return renderExpression(allocator, stream, tree, indent, start_col, addr_of.rhs, space); | |
| 500 | }, | |
| 501 | .Try => { | |
| 502 | const try_node = @fieldParentPtr(ast.Node.Try, "base", base); | |
| 503 | try renderToken(tree, stream, try_node.op_token, indent, start_col, Space.Space); | |
| 504 | return renderExpression(allocator, stream, tree, indent, start_col, try_node.rhs, space); | |
| 505 | }, | |
| 506 | .Resume => { | |
| 507 | const resume_node = @fieldParentPtr(ast.Node.Resume, "base", base); | |
| 508 | try renderToken(tree, stream, resume_node.op_token, indent, start_col, Space.Space); | |
| 509 | return renderExpression(allocator, stream, tree, indent, start_col, resume_node.rhs, space); | |
| 510 | }, | |
| 511 | .Await => { | |
| 512 | const await_node = @fieldParentPtr(ast.Node.Await, "base", base); | |
| 513 | try renderToken(tree, stream, await_node.op_token, indent, start_col, Space.Space); | |
| 514 | return renderExpression(allocator, stream, tree, indent, start_col, await_node.rhs, space); | |
| 515 | }, | |
| 514 | 516 | |
| 515 | try renderToken(tree, stream, colon1, indent, start_col, Space.None); // : | |
| 516 | try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None); | |
| 517 | try renderToken(tree, stream, colon2, indent, start_col, Space.None); // : | |
| 518 | try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None); | |
| 517 | .ArrayType => { | |
| 518 | const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base); | |
| 519 | return renderArrayType( | |
| 520 | allocator, | |
| 521 | stream, | |
| 522 | tree, | |
| 523 | indent, | |
| 524 | start_col, | |
| 525 | array_type.op_token, | |
| 526 | array_type.rhs, | |
| 527 | array_type.len_expr, | |
| 528 | null, | |
| 529 | space, | |
| 530 | ); | |
| 531 | }, | |
| 532 | .ArrayTypeSentinel => { | |
| 533 | const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base); | |
| 534 | return renderArrayType( | |
| 535 | allocator, | |
| 536 | stream, | |
| 537 | tree, | |
| 538 | indent, | |
| 539 | start_col, | |
| 540 | array_type.op_token, | |
| 541 | array_type.rhs, | |
| 542 | array_type.len_expr, | |
| 543 | array_type.sentinel, | |
| 544 | space, | |
| 545 | ); | |
| 546 | }, | |
| 519 | 547 | |
| 520 | const rparen_token = tree.nextToken(bit_range.end.lastToken()); | |
| 521 | try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) | |
| 522 | } else { | |
| 523 | const rparen_token = tree.nextToken(align_info.node.lastToken()); | |
| 524 | try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) | |
| 525 | } | |
| 526 | } | |
| 527 | if (ptr_info.const_token) |const_token| { | |
| 528 | try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const | |
| 529 | } | |
| 530 | if (ptr_info.volatile_token) |volatile_token| { | |
| 531 | try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile | |
| 532 | } | |
| 533 | }, | |
| 548 | .PtrType => { | |
| 549 | const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base); | |
| 550 | const op_tok_id = tree.token_ids[ptr_type.op_token]; | |
| 551 | switch (op_tok_id) { | |
| 552 | .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'), | |
| 553 | .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier) | |
| 554 | try stream.writeAll("[*c") | |
| 555 | else | |
| 556 | try stream.writeAll("[*"), | |
| 557 | else => unreachable, | |
| 558 | } | |
| 559 | if (ptr_type.ptr_info.sentinel) |sentinel| { | |
| 560 | const colon_token = tree.prevToken(sentinel.firstToken()); | |
| 561 | try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : | |
| 562 | const sentinel_space = switch (op_tok_id) { | |
| 563 | .LBracket => Space.None, | |
| 564 | else => Space.Space, | |
| 565 | }; | |
| 566 | try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space); | |
| 567 | } | |
| 568 | switch (op_tok_id) { | |
| 569 | .Asterisk, .AsteriskAsterisk => {}, | |
| 570 | .LBracket => try stream.writeByte(']'), | |
| 571 | else => unreachable, | |
| 572 | } | |
| 573 | if (ptr_type.ptr_info.allowzero_token) |allowzero_token| { | |
| 574 | try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero | |
| 575 | } | |
| 576 | if (ptr_type.ptr_info.align_info) |align_info| { | |
| 577 | const lparen_token = tree.prevToken(align_info.node.firstToken()); | |
| 578 | const align_token = tree.prevToken(lparen_token); | |
| 534 | 579 | |
| 535 | .SliceType => |ptr_info| { | |
| 536 | try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [ | |
| 537 | if (ptr_info.sentinel) |sentinel| { | |
| 538 | const colon_token = tree.prevToken(sentinel.firstToken()); | |
| 539 | try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : | |
| 540 | try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None); | |
| 541 | try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ] | |
| 542 | } else { | |
| 543 | try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ] | |
| 544 | } | |
| 580 | try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align | |
| 581 | try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( | |
| 545 | 582 | |
| 546 | if (ptr_info.allowzero_token) |allowzero_token| { | |
| 547 | try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero | |
| 548 | } | |
| 549 | if (ptr_info.align_info) |align_info| { | |
| 550 | const lparen_token = tree.prevToken(align_info.node.firstToken()); | |
| 551 | const align_token = tree.prevToken(lparen_token); | |
| 583 | try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None); | |
| 552 | 584 | |
| 553 | try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align | |
| 554 | try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( | |
| 585 | if (align_info.bit_range) |bit_range| { | |
| 586 | const colon1 = tree.prevToken(bit_range.start.firstToken()); | |
| 587 | const colon2 = tree.prevToken(bit_range.end.firstToken()); | |
| 555 | 588 | |
| 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); | |
| 557 | 593 | |
| 558 | if (align_info.bit_range) |bit_range| { | |
| 559 | const colon1 = tree.prevToken(bit_range.start.firstToken()); | |
| 560 | const colon2 = tree.prevToken(bit_range.end.firstToken()); | |
| 594 | const rparen_token = tree.nextToken(bit_range.end.lastToken()); | |
| 595 | try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) | |
| 596 | } else { | |
| 597 | const rparen_token = tree.nextToken(align_info.node.lastToken()); | |
| 598 | try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) | |
| 599 | } | |
| 600 | } | |
| 601 | if (ptr_type.ptr_info.const_token) |const_token| { | |
| 602 | try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const | |
| 603 | } | |
| 604 | if (ptr_type.ptr_info.volatile_token) |volatile_token| { | |
| 605 | try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile | |
| 606 | } | |
| 607 | return renderExpression(allocator, stream, tree, indent, start_col, ptr_type.rhs, space); | |
| 608 | }, | |
| 561 | 609 | |
| 562 | try renderToken(tree, stream, colon1, indent, start_col, Space.None); // : | |
| 563 | try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None); | |
| 564 | try renderToken(tree, stream, colon2, indent, start_col, Space.None); // : | |
| 565 | try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None); | |
| 610 | .SliceType => { | |
| 611 | const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base); | |
| 612 | try renderToken(tree, stream, slice_type.op_token, indent, start_col, Space.None); // [ | |
| 613 | if (slice_type.ptr_info.sentinel) |sentinel| { | |
| 614 | const colon_token = tree.prevToken(sentinel.firstToken()); | |
| 615 | try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : | |
| 616 | try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None); | |
| 617 | try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ] | |
| 618 | } else { | |
| 619 | try renderToken(tree, stream, tree.nextToken(slice_type.op_token), indent, start_col, Space.None); // ] | |
| 620 | } | |
| 566 | 621 | |
| 567 | const rparen_token = tree.nextToken(bit_range.end.lastToken()); | |
| 568 | try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) | |
| 569 | } else { | |
| 570 | const rparen_token = tree.nextToken(align_info.node.lastToken()); | |
| 571 | try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) | |
| 572 | } | |
| 573 | } | |
| 574 | if (ptr_info.const_token) |const_token| { | |
| 575 | try renderToken(tree, stream, const_token, indent, start_col, Space.Space); | |
| 576 | } | |
| 577 | if (ptr_info.volatile_token) |volatile_token| { | |
| 578 | try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); | |
| 579 | } | |
| 580 | }, | |
| 622 | if (slice_type.ptr_info.allowzero_token) |allowzero_token| { | |
| 623 | try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero | |
| 624 | } | |
| 625 | if (slice_type.ptr_info.align_info) |align_info| { | |
| 626 | const lparen_token = tree.prevToken(align_info.node.firstToken()); | |
| 627 | const align_token = tree.prevToken(lparen_token); | |
| 581 | 628 | |
| 582 | .ArrayType => |array_info| { | |
| 583 | const lbracket = prefix_op_node.op_token; | |
| 584 | const rbracket = tree.nextToken(if (array_info.sentinel) |sentinel| | |
| 585 | sentinel.lastToken() | |
| 586 | else | |
| 587 | array_info.len_expr.lastToken()); | |
| 629 | try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align | |
| 630 | try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( | |
| 588 | 631 | |
| 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); | |
| 590 | 633 | |
| 591 | const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment; | |
| 592 | const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment; | |
| 593 | const new_indent = if (ends_with_comment) indent + indent_delta else indent; | |
| 594 | const new_space = if (ends_with_comment) Space.Newline else Space.None; | |
| 595 | try renderExpression(allocator, stream, tree, new_indent, start_col, array_info.len_expr, new_space); | |
| 596 | if (starts_with_comment) { | |
| 597 | try stream.writeByte('\n'); | |
| 598 | } | |
| 599 | if (ends_with_comment or starts_with_comment) { | |
| 600 | try stream.writeByteNTimes(' ', indent); | |
| 601 | } | |
| 602 | if (array_info.sentinel) |sentinel| { | |
| 603 | const colon_token = tree.prevToken(sentinel.firstToken()); | |
| 604 | try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : | |
| 605 | try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None); | |
| 606 | } | |
| 607 | try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ] | |
| 608 | }, | |
| 609 | .BitNot, | |
| 610 | .BoolNot, | |
| 611 | .Negation, | |
| 612 | .NegationWrap, | |
| 613 | .OptionalType, | |
| 614 | .AddressOf, | |
| 615 | => { | |
| 616 | try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); | |
| 617 | }, | |
| 634 | if (align_info.bit_range) |bit_range| { | |
| 635 | const colon1 = tree.prevToken(bit_range.start.firstToken()); | |
| 636 | const colon2 = tree.prevToken(bit_range.end.firstToken()); | |
| 618 | 637 | |
| 619 | .Try, | |
| 620 | .Resume, | |
| 621 | => { | |
| 622 | try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space); | |
| 623 | }, | |
| 638 | try renderToken(tree, stream, colon1, indent, start_col, Space.None); // : | |
| 639 | try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None); | |
| 640 | try renderToken(tree, stream, colon2, indent, start_col, Space.None); // : | |
| 641 | try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None); | |
| 624 | 642 | |
| 625 | .Await => |await_info| { | |
| 626 | try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space); | |
| 627 | }, | |
| 643 | const rparen_token = tree.nextToken(bit_range.end.lastToken()); | |
| 644 | try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) | |
| 645 | } else { | |
| 646 | const rparen_token = tree.nextToken(align_info.node.lastToken()); | |
| 647 | try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) | |
| 648 | } | |
| 628 | 649 | } |
| 629 | ||
| 630 | return renderExpression(allocator, stream, tree, indent, start_col, prefix_op_node.rhs, space); | |
| 650 | if (slice_type.ptr_info.const_token) |const_token| { | |
| 651 | try renderToken(tree, stream, const_token, indent, start_col, Space.Space); | |
| 652 | } | |
| 653 | if (slice_type.ptr_info.volatile_token) |volatile_token| { | |
| 654 | try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); | |
| 655 | } | |
| 656 | return renderExpression(allocator, stream, tree, indent, start_col, slice_type.rhs, space); | |
| 631 | 657 | }, |
| 632 | 658 | |
| 633 | 659 | .ArrayInitializer, .ArrayInitializerDot => { |
| ... | ... | @@ -2057,6 +2083,46 @@ fn renderExpression( |
| 2057 | 2083 | } |
| 2058 | 2084 | } |
| 2059 | 2085 | |
| 2086 | fn 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 | ||
| 2060 | 2126 | fn renderAsmOutput( |
| 2061 | 2127 | allocator: *mem.Allocator, |
| 2062 | 2128 | stream: anytype, |
src-self-hosted/Module.zig+48-23| ... | ... | @@ -36,7 +36,7 @@ bin_file_path: []const u8, |
| 36 | 36 | decl_exports: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{}, |
| 37 | 37 | /// We track which export is associated with the given symbol name for quick |
| 38 | 38 | /// detection of symbol collisions. |
| 39 | symbol_exports: std.StringHashMap(*Export), | |
| 39 | symbol_exports: std.StringHashMapUnmanaged(*Export) = .{}, | |
| 40 | 40 | /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl |
| 41 | 41 | /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that |
| 42 | 42 | /// is performing the export of another Decl. |
| ... | ... | @@ -769,7 +769,6 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 769 | 769 | .bin_file_path = options.bin_file_path, |
| 770 | 770 | .bin_file = bin_file, |
| 771 | 771 | .optimize_mode = options.optimize_mode, |
| 772 | .symbol_exports = std.StringHashMap(*Export).init(gpa), | |
| 773 | 772 | .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), |
| 774 | 773 | .keep_source_files_loaded = options.keep_source_files_loaded, |
| 775 | 774 | }; |
| ... | ... | @@ -812,7 +811,7 @@ pub fn deinit(self: *Module) void { |
| 812 | 811 | } |
| 813 | 812 | self.export_owners.deinit(gpa); |
| 814 | 813 | |
| 815 | self.symbol_exports.deinit(); | |
| 814 | self.symbol_exports.deinit(gpa); | |
| 816 | 815 | self.root_scope.destroy(gpa); |
| 817 | 816 | self.* = undefined; |
| 818 | 817 | } |
| ... | ... | @@ -1309,10 +1308,18 @@ fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir |
| 1309 | 1308 | .ControlFlowExpression => return self.astGenControlFlowExpression(scope, @fieldParentPtr(ast.Node.ControlFlowExpression, "base", ast_node)), |
| 1310 | 1309 | .If => return self.astGenIf(scope, @fieldParentPtr(ast.Node.If, "base", ast_node)), |
| 1311 | 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 | 1312 | else => return self.failNode(scope, ast_node, "TODO implement astGenExpr for {}", .{@tagName(ast_node.id)}), |
| 1313 | 1313 | } |
| 1314 | 1314 | } |
| 1315 | 1315 | |
| 1316 | fn 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 | ||
| 1316 | 1323 | fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) InnerError!*zir.Inst { |
| 1317 | 1324 | switch (infix_node.op) { |
| 1318 | 1325 | .Assign => { |
| ... | ... | @@ -1351,17 +1358,19 @@ fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) In |
| 1351 | 1358 | const tree = scope.tree(); |
| 1352 | 1359 | const src = tree.token_locs[infix_node.op_token].start; |
| 1353 | 1360 | |
| 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 | 1371 | return self.addZIRInst(scope, src, zir.Inst.Cmp, .{ |
| 1355 | 1372 | .lhs = lhs, |
| 1356 | .op = @as(std.math.CompareOperator, switch (infix_node.op) { | |
| 1357 | .BangEqual => .neq, | |
| 1358 | .EqualEqual => .eq, | |
| 1359 | .GreaterThan => .gt, | |
| 1360 | .GreaterOrEqual => .gte, | |
| 1361 | .LessThan => .lt, | |
| 1362 | .LessOrEqual => .lte, | |
| 1363 | else => unreachable, | |
| 1364 | }), | |
| 1373 | .op = op, | |
| 1365 | 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 | 1417 | defer then_scope.instructions.deinit(self.gpa); |
| 1409 | 1418 | |
| 1410 | 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; | |
| 1412 | _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{ | |
| 1413 | .block = block, | |
| 1414 | .operand = then_result, | |
| 1415 | }, .{}); | |
| 1420 | if (!then_result.tag.isNoReturn()) { | |
| 1421 | const then_src = tree.token_locs[if_node.body.lastToken()].start; | |
| 1422 | _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{ | |
| 1423 | .block = block, | |
| 1424 | .operand = then_result, | |
| 1425 | }, .{}); | |
| 1426 | } | |
| 1416 | 1427 | condbr.positionals.true_body = .{ |
| 1417 | 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 | 1437 | |
| 1427 | 1438 | if (if_node.@"else") |else_node| { |
| 1428 | 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; | |
| 1430 | _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{ | |
| 1431 | .block = block, | |
| 1432 | .operand = else_result, | |
| 1433 | }, .{}); | |
| 1440 | if (!else_result.tag.isNoReturn()) { | |
| 1441 | const else_src = tree.token_locs[else_node.body.lastToken()].start; | |
| 1442 | _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{ | |
| 1443 | .block = block, | |
| 1444 | .operand = else_result, | |
| 1445 | }, .{}); | |
| 1446 | } | |
| 1434 | 1447 | } else { |
| 1435 | 1448 | // TODO Optimization opportunity: we can avoid an allocation and a memcpy here |
| 1436 | 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 | 2318 | return; |
| 2306 | 2319 | } |
| 2307 | 2320 | |
| 2308 | try self.symbol_exports.putNoClobber(symbol_name, new_export); | |
| 2321 | try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export); | |
| 2309 | 2322 | self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) { |
| 2310 | 2323 | error.OutOfMemory => return error.OutOfMemory, |
| 2311 | 2324 | else => { |
| ... | ... | @@ -2559,6 +2572,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In |
| 2559 | 2572 | .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?), |
| 2560 | 2573 | .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?), |
| 2561 | 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 | } |
| 2564 | 2578 | |
| ... | ... | @@ -3236,6 +3250,17 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError! |
| 3236 | 3250 | return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{}); |
| 3237 | 3251 | } |
| 3238 | 3252 | |
| 3253 | fn 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 | ||
| 3239 | 3264 | fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst { |
| 3240 | 3265 | const operand = try self.resolveInst(scope, inst.positionals.operand); |
| 3241 | 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 | 407 | .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch), |
| 408 | 408 | .sub => return self.genSub(inst.cast(ir.Inst.Sub).?, arch), |
| 409 | 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 | } |
| 412 | 461 | |
| ... | ... | @@ -434,7 +483,7 @@ const Function = struct { |
| 434 | 483 | } |
| 435 | 484 | } |
| 436 | 485 | |
| 437 | /// ADD, SUB | |
| 486 | /// ADD, SUB, XOR, OR, AND | |
| 438 | 487 | fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue { |
| 439 | 488 | try self.code.ensureCapacity(self.code.items.len + 8); |
| 440 | 489 | |
| ... | ... | @@ -695,7 +744,7 @@ const Function = struct { |
| 695 | 744 | |
| 696 | 745 | fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue { |
| 697 | 746 | switch (arch) { |
| 698 | .i386, .x86_64 => { | |
| 747 | .x86_64 => { | |
| 699 | 748 | try self.code.ensureCapacity(self.code.items.len + 6); |
| 700 | 749 | |
| 701 | 750 | const cond = try self.resolveInst(inst.args.condition); |
| ... | ... | @@ -724,7 +773,20 @@ const Function = struct { |
| 724 | 773 | }; |
| 725 | 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 | 792 | else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}), |
| ... | ... | @@ -812,6 +874,8 @@ const Function = struct { |
| 812 | 874 | } |
| 813 | 875 | |
| 814 | 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 | 879 | if (arch != .x86_64 and arch != .i386) { |
| 816 | 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 | 944 | .none => unreachable, |
| 881 | 945 | .unreach => unreachable, |
| 882 | 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 | 960 | .compare_flags_signed => |op| { |
| 886 | 961 | return self.fail(src, "TODO set register with compare flags value (signed)", .{}); |
| ... | ... | @@ -1135,6 +1210,9 @@ const Function = struct { |
| 1135 | 1210 | } |
| 1136 | 1211 | return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; |
| 1137 | 1212 | }, |
| 1213 | .Bool => { | |
| 1214 | return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; | |
| 1215 | }, | |
| 1138 | 1216 | .ComptimeInt => unreachable, // semantic analysis prevents this |
| 1139 | 1217 | .ComptimeFloat => unreachable, // semantic analysis prevents this |
| 1140 | 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 | 14 | tag: Tag, |
| 15 | 15 | /// Each bit represents the index of an `Inst` parameter in the `args` field. |
| 16 | 16 | /// If a bit is set, it marks the end of the lifetime of the corresponding |
| 17 | /// instruction parameter. For example, 0b000_00101 means that the first and | |
| 17 | /// instruction parameter. For example, 0b101 means that the first and | |
| 18 | 18 | /// third `Inst` parameters' lifetimes end after this instruction, and will |
| 19 | 19 | /// not have any more following references. |
| 20 | 20 | /// The most significant bit being set means that the instruction itself is |
| 21 | 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. | |
| 23 | /// If bit 6 (0bx1xx_xxxx) is set, it means this is a special case and the | |
| 22 | /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced. | |
| 23 | /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the | |
| 24 | 24 | /// lifetimes of operands are encoded elsewhere. |
| 25 | deaths: u8 = undefined, | |
| 25 | deaths: DeathsInt = undefined, | |
| 26 | 26 | ty: Type, |
| 27 | 27 | /// Byte offset into the source. |
| 28 | 28 | src: usize, |
| 29 | 29 | |
| 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 | 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 | } |
| 33 | 38 | |
| 34 | pub fn operandDies(self: Inst, index: u3) bool { | |
| 35 | assert(index < 6); | |
| 39 | pub fn operandDies(self: Inst, index: DeathsBitIndex) bool { | |
| 40 | assert(index < deaths_bits); | |
| 36 | 41 | return @truncate(u1, self.deaths << index) != 0; |
| 37 | 42 | } |
| 38 | 43 | |
| 39 | 44 | pub fn specialOperandDeaths(self: Inst) bool { |
| 40 | return (self.deaths & 0b1000_0000) != 0; | |
| 45 | return (self.deaths & (1 << deaths_bits)) != 0; | |
| 41 | 46 | } |
| 42 | 47 | |
| 43 | 48 | pub const Tag = enum { |
| ... | ... | @@ -60,6 +65,7 @@ pub const Inst = struct { |
| 60 | 65 | retvoid, |
| 61 | 66 | sub, |
| 62 | 67 | unreach, |
| 68 | not, | |
| 63 | 69 | }; |
| 64 | 70 | |
| 65 | 71 | pub fn cast(base: *Inst, comptime T: type) ?*T { |
| ... | ... | @@ -194,6 +200,15 @@ pub const Inst = struct { |
| 194 | 200 | false_death_count: u32 = 0, |
| 195 | 201 | }; |
| 196 | 202 | |
| 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 | 212 | pub const Constant = struct { |
| 198 | 213 | pub const base_tag = Tag.constant; |
| 199 | 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 | 34 | inline for (std.meta.declarations(ir.Inst)) |decl| { |
| 35 | 35 | switch (decl.data) { |
| 36 | 36 | .Type => |T| { |
| 37 | if (@hasDecl(T, "base_tag")) { | |
| 37 | if (@typeInfo(T) == .Struct and @hasDecl(T, "base_tag")) { | |
| 38 | 38 | if (T.base_tag == base.tag) { |
| 39 | 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 | 47 | } |
| 48 | 48 | |
| 49 | 49 | fn 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 | } | |
| 51 | 57 | |
| 52 | 58 | switch (T) { |
| 53 | 59 | ir.Inst.Constant => return, |
| ... | ... | @@ -106,15 +112,28 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void |
| 106 | 112 | // instruction, and the deaths flag for the CondBr instruction will indicate whether the |
| 107 | 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 | 134 | else => {}, |
| 110 | 135 | } |
| 111 | 136 | |
| 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 | 137 | const Args = ir.Inst.Args(T); |
| 119 | 138 | if (Args == void) { |
| 120 | 139 | return; |
src-self-hosted/translate_c.zig+80-87| ... | ... | @@ -1561,7 +1561,7 @@ fn transImplicitCastExpr( |
| 1561 | 1561 | return maybeSuppressResult(rp, scope, result_used, sub_expr_node); |
| 1562 | 1562 | } |
| 1563 | 1563 | |
| 1564 | const prefix_op = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); | |
| 1564 | const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); | |
| 1565 | 1565 | prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value); |
| 1566 | 1566 | |
| 1567 | 1567 | return maybeSuppressResult(rp, scope, result_used, &prefix_op.base); |
| ... | ... | @@ -1673,11 +1673,7 @@ fn isBoolRes(res: *ast.Node) bool { |
| 1673 | 1673 | |
| 1674 | 1674 | else => {}, |
| 1675 | 1675 | }, |
| 1676 | .PrefixOp => switch (@fieldParentPtr(ast.Node.PrefixOp, "base", res).op) { | |
| 1677 | .BoolNot => return true, | |
| 1678 | ||
| 1679 | else => {}, | |
| 1680 | }, | |
| 1676 | .BoolNot => return true, | |
| 1681 | 1677 | .BoolLiteral => return true, |
| 1682 | 1678 | .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr), |
| 1683 | 1679 | else => {}, |
| ... | ... | @@ -2162,21 +2158,16 @@ fn transCreateNodeArrayType( |
| 2162 | 2158 | source_loc: ZigClangSourceLocation, |
| 2163 | 2159 | ty: *const ZigClangType, |
| 2164 | 2160 | len: anytype, |
| 2165 | ) TransError!*ast.Node { | |
| 2166 | var node = try transCreateNodePrefixOp( | |
| 2167 | rp.c, | |
| 2168 | .{ | |
| 2169 | .ArrayType = .{ | |
| 2170 | .len_expr = undefined, | |
| 2171 | .sentinel = null, | |
| 2172 | }, | |
| 2173 | }, | |
| 2174 | .LBracket, | |
| 2175 | "[", | |
| 2176 | ); | |
| 2177 | node.op.ArrayType.len_expr = try transCreateNodeInt(rp.c, len); | |
| 2161 | ) !*ast.Node { | |
| 2162 | const node = try rp.c.arena.create(ast.Node.ArrayType); | |
| 2163 | const op_token = try appendToken(rp.c, .LBracket, "["); | |
| 2164 | const len_expr = try transCreateNodeInt(rp.c, len); | |
| 2178 | 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 | 2171 | return &node.base; |
| 2181 | 2172 | } |
| 2182 | 2173 | |
| ... | ... | @@ -2449,7 +2440,7 @@ fn transDoWhileLoop( |
| 2449 | 2440 | }, |
| 2450 | 2441 | }; |
| 2451 | 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 | 2444 | prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true); |
| 2454 | 2445 | _ = try appendToken(rp.c, .RParen, ")"); |
| 2455 | 2446 | if_node.condition = &prefix_op.base; |
| ... | ... | @@ -3036,7 +3027,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar |
| 3036 | 3027 | else |
| 3037 | 3028 | return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used), |
| 3038 | 3029 | .AddrOf => { |
| 3039 | const op_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); | |
| 3030 | const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); | |
| 3040 | 3031 | op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value); |
| 3041 | 3032 | return &op_node.base; |
| 3042 | 3033 | }, |
| ... | ... | @@ -3052,7 +3043,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar |
| 3052 | 3043 | .Plus => return transExpr(rp, scope, op_expr, used, .r_value), |
| 3053 | 3044 | .Minus => { |
| 3054 | 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 | 3047 | op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); |
| 3057 | 3048 | return &op_node.base; |
| 3058 | 3049 | } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) { |
| ... | ... | @@ -3065,12 +3056,12 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar |
| 3065 | 3056 | return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{}); |
| 3066 | 3057 | }, |
| 3067 | 3058 | .Not => { |
| 3068 | const op_node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~"); | |
| 3059 | const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~"); | |
| 3069 | 3060 | op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); |
| 3070 | 3061 | return &op_node.base; |
| 3071 | 3062 | }, |
| 3072 | 3063 | .LNot => { |
| 3073 | const op_node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); | |
| 3064 | const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!"); | |
| 3074 | 3065 | op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true); |
| 3075 | 3066 | return &op_node.base; |
| 3076 | 3067 | }, |
| ... | ... | @@ -3116,7 +3107,7 @@ fn transCreatePreCrement( |
| 3116 | 3107 | |
| 3117 | 3108 | const node = try transCreateNodeVarDecl(rp.c, false, true, ref); |
| 3118 | 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 | 3111 | rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); |
| 3121 | 3112 | node.init_node = &rhs_node.base; |
| 3122 | 3113 | node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); |
| ... | ... | @@ -3182,7 +3173,7 @@ fn transCreatePostCrement( |
| 3182 | 3173 | |
| 3183 | 3174 | const node = try transCreateNodeVarDecl(rp.c, false, true, ref); |
| 3184 | 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 | 3177 | rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); |
| 3187 | 3178 | node.init_node = &rhs_node.base; |
| 3188 | 3179 | node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); |
| ... | ... | @@ -3336,7 +3327,7 @@ fn transCreateCompoundAssign( |
| 3336 | 3327 | |
| 3337 | 3328 | const node = try transCreateNodeVarDecl(rp.c, false, true, ref); |
| 3338 | 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 | 3331 | addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value); |
| 3341 | 3332 | node.init_node = &addr_node.base; |
| 3342 | 3333 | node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); |
| ... | ... | @@ -3984,16 +3975,15 @@ fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []c |
| 3984 | 3975 | return &field_access_node.base; |
| 3985 | 3976 | } |
| 3986 | 3977 | |
| 3987 | fn transCreateNodePrefixOp( | |
| 3978 | fn transCreateNodeSimplePrefixOp( | |
| 3988 | 3979 | c: *Context, |
| 3989 | op: ast.Node.PrefixOp.Op, | |
| 3980 | comptime tag: ast.Node.Id, | |
| 3990 | 3981 | op_tok_id: std.zig.Token.Id, |
| 3991 | 3982 | bytes: []const u8, |
| 3992 | ) !*ast.Node.PrefixOp { | |
| 3993 | const node = try c.arena.create(ast.Node.PrefixOp); | |
| 3983 | ) !*ast.Node.SimplePrefixOp(tag) { | |
| 3984 | const node = try c.arena.create(ast.Node.SimplePrefixOp(tag)); | |
| 3994 | 3985 | node.* = .{ |
| 3995 | 3986 | .op_token = try appendToken(c, op_tok_id, bytes), |
| 3996 | .op = op, | |
| 3997 | 3987 | .rhs = undefined, // translate and set afterward |
| 3998 | 3988 | }; |
| 3999 | 3989 | return node; |
| ... | ... | @@ -4065,8 +4055,8 @@ fn transCreateNodePtrType( |
| 4065 | 4055 | is_const: bool, |
| 4066 | 4056 | is_volatile: bool, |
| 4067 | 4057 | op_tok_id: std.zig.Token.Id, |
| 4068 | ) !*ast.Node.PrefixOp { | |
| 4069 | const node = try c.arena.create(ast.Node.PrefixOp); | |
| 4058 | ) !*ast.Node.PtrType { | |
| 4059 | const node = try c.arena.create(ast.Node.PtrType); | |
| 4070 | 4060 | const op_token = switch (op_tok_id) { |
| 4071 | 4061 | .LBracket => blk: { |
| 4072 | 4062 | const lbracket = try appendToken(c, .LBracket, "["); |
| ... | ... | @@ -4086,11 +4076,9 @@ fn transCreateNodePtrType( |
| 4086 | 4076 | }; |
| 4087 | 4077 | node.* = .{ |
| 4088 | 4078 | .op_token = op_token, |
| 4089 | .op = .{ | |
| 4090 | .PtrType = .{ | |
| 4091 | .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null, | |
| 4092 | .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null, | |
| 4093 | }, | |
| 4079 | .ptr_info = .{ | |
| 4080 | .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null, | |
| 4081 | .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null, | |
| 4094 | 4082 | }, |
| 4095 | 4083 | .rhs = undefined, // translate and set afterward |
| 4096 | 4084 | }; |
| ... | ... | @@ -4569,12 +4557,12 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour |
| 4569 | 4557 | .Pointer => { |
| 4570 | 4558 | const child_qt = ZigClangType_getPointeeType(ty); |
| 4571 | 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 | 4561 | optional_node.rhs = try transQualType(rp, child_qt, source_loc); |
| 4574 | 4562 | return &optional_node.base; |
| 4575 | 4563 | } |
| 4576 | 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 | 4566 | const pointer_node = try transCreateNodePtrType( |
| 4579 | 4567 | rp.c, |
| 4580 | 4568 | ZigClangQualType_isConstQualified(child_qt), |
| ... | ... | @@ -4599,21 +4587,8 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour |
| 4599 | 4587 | |
| 4600 | 4588 | const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty); |
| 4601 | 4589 | const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize)); |
| 4602 | var node = try transCreateNodePrefixOp( | |
| 4603 | rp.c, | |
| 4604 | .{ | |
| 4605 | .ArrayType = .{ | |
| 4606 | .len_expr = undefined, | |
| 4607 | .sentinel = null, | |
| 4608 | }, | |
| 4609 | }, | |
| 4610 | .LBracket, | |
| 4611 | "[", | |
| 4612 | ); | |
| 4613 | node.op.ArrayType.len_expr = try transCreateNodeInt(rp.c, size); | |
| 4614 | _ = try appendToken(rp.c, .RBracket, "]"); | |
| 4615 | node.rhs = try transQualType(rp, ZigClangConstantArrayType_getElementType(const_arr_ty), source_loc); | |
| 4616 | return &node.base; | |
| 4590 | const elem_ty = ZigClangQualType_getTypePtr(ZigClangConstantArrayType_getElementType(const_arr_ty)); | |
| 4591 | return try transCreateNodeArrayType(rp, source_loc, elem_ty, size); | |
| 4617 | 4592 | }, |
| 4618 | 4593 | .IncompleteArray => { |
| 4619 | 4594 | const incomplete_array_ty = @ptrCast(*const ZigClangIncompleteArrayType, ty); |
| ... | ... | @@ -5824,7 +5799,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, |
| 5824 | 5799 | if (prev_id == .Keyword_void) { |
| 5825 | 5800 | const ptr = try transCreateNodePtrType(c, false, false, .Asterisk); |
| 5826 | 5801 | ptr.rhs = node; |
| 5827 | const optional_node = try transCreateNodePrefixOp(c, .OptionalType, .QuestionMark, "?"); | |
| 5802 | const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?"); | |
| 5828 | 5803 | optional_node.rhs = &ptr.base; |
| 5829 | 5804 | return &optional_node.base; |
| 5830 | 5805 | } else { |
| ... | ... | @@ -5993,18 +5968,18 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, |
| 5993 | 5968 | |
| 5994 | 5969 | switch (op_tok.id) { |
| 5995 | 5970 | .Bang => { |
| 5996 | const node = try transCreateNodePrefixOp(c, .BoolNot, .Bang, "!"); | |
| 5971 | const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!"); | |
| 5997 | 5972 | node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope); |
| 5998 | 5973 | return &node.base; |
| 5999 | 5974 | }, |
| 6000 | 5975 | .Minus => { |
| 6001 | const node = try transCreateNodePrefixOp(c, .Negation, .Minus, "-"); | |
| 5976 | const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-"); | |
| 6002 | 5977 | node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope); |
| 6003 | 5978 | return &node.base; |
| 6004 | 5979 | }, |
| 6005 | 5980 | .Plus => return try parseCPrefixOpExpr(c, it, source, source_loc, scope), |
| 6006 | 5981 | .Tilde => { |
| 6007 | const node = try transCreateNodePrefixOp(c, .BitNot, .Tilde, "~"); | |
| 5982 | const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~"); | |
| 6008 | 5983 | node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope); |
| 6009 | 5984 | return &node.base; |
| 6010 | 5985 | }, |
| ... | ... | @@ -6013,7 +5988,7 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, |
| 6013 | 5988 | return try transCreateNodePtrDeref(c, node); |
| 6014 | 5989 | }, |
| 6015 | 5990 | .Ampersand => { |
| 6016 | const node = try transCreateNodePrefixOp(c, .AddressOf, .Ampersand, "&"); | |
| 5991 | const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&"); | |
| 6017 | 5992 | node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope); |
| 6018 | 5993 | return &node.base; |
| 6019 | 5994 | }, |
| ... | ... | @@ -6034,29 +6009,49 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 { |
| 6034 | 6009 | } |
| 6035 | 6010 | |
| 6036 | 6011 | fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { |
| 6037 | if (node.id == .ContainerDecl) { | |
| 6038 | return node; | |
| 6039 | } else if (node.id == .PrefixOp) { | |
| 6040 | return node; | |
| 6041 | } else if (node.cast(ast.Node.Identifier)) |ident| { | |
| 6042 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { | |
| 6043 | if (value.cast(ast.Node.VarDecl)) |var_decl| | |
| 6044 | return getContainer(c, var_decl.init_node.?); | |
| 6045 | } | |
| 6046 | } else if (node.cast(ast.Node.InfixOp)) |infix| { | |
| 6047 | if (infix.op != .Period) | |
| 6048 | return null; | |
| 6049 | if (getContainerTypeOf(c, infix.lhs)) |ty_node| { | |
| 6050 | if (ty_node.cast(ast.Node.ContainerDecl)) |container| { | |
| 6051 | for (container.fieldsAndDecls()) |field_ref| { | |
| 6052 | const field = field_ref.cast(ast.Node.ContainerField).?; | |
| 6053 | const ident = infix.rhs.cast(ast.Node.Identifier).?; | |
| 6054 | if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { | |
| 6055 | return getContainer(c, field.type_expr.?); | |
| 6012 | switch (node.id) { | |
| 6013 | .ContainerDecl, | |
| 6014 | .AddressOf, | |
| 6015 | .Await, | |
| 6016 | .BitNot, | |
| 6017 | .BoolNot, | |
| 6018 | .OptionalType, | |
| 6019 | .Negation, | |
| 6020 | .NegationWrap, | |
| 6021 | .Resume, | |
| 6022 | .Try, | |
| 6023 | .ArrayType, | |
| 6024 | .ArrayTypeSentinel, | |
| 6025 | .PtrType, | |
| 6026 | .SliceType, | |
| 6027 | => return node, | |
| 6028 | ||
| 6029 | .Identifier => { | |
| 6030 | const ident = node.cast(ast.Node.Identifier).?; | |
| 6031 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { | |
| 6032 | if (value.cast(ast.Node.VarDecl)) |var_decl| | |
| 6033 | return getContainer(c, var_decl.init_node.?); | |
| 6034 | } | |
| 6035 | }, | |
| 6036 | ||
| 6037 | .InfixOp => { | |
| 6038 | const infix = node.cast(ast.Node.InfixOp).?; | |
| 6039 | if (infix.op != .Period) | |
| 6040 | return null; | |
| 6041 | if (getContainerTypeOf(c, infix.lhs)) |ty_node| { | |
| 6042 | if (ty_node.cast(ast.Node.ContainerDecl)) |container| { | |
| 6043 | for (container.fieldsAndDecls()) |field_ref| { | |
| 6044 | const field = field_ref.cast(ast.Node.ContainerField).?; | |
| 6045 | const ident = infix.rhs.cast(ast.Node.Identifier).?; | |
| 6046 | if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { | |
| 6047 | return getContainer(c, field.type_expr.?); | |
| 6048 | } | |
| 6056 | 6049 | } |
| 6057 | 6050 | } |
| 6058 | 6051 | } |
| 6059 | } | |
| 6052 | }, | |
| 6053 | ||
| 6054 | else => {}, | |
| 6060 | 6055 | } |
| 6061 | 6056 | return null; |
| 6062 | 6057 | } |
| ... | ... | @@ -6091,11 +6086,9 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { |
| 6091 | 6086 | fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto { |
| 6092 | 6087 | const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null; |
| 6093 | 6088 | if (getContainerTypeOf(c, init)) |ty_node| { |
| 6094 | if (ty_node.cast(ast.Node.PrefixOp)) |prefix| { | |
| 6095 | if (prefix.op == .OptionalType) { | |
| 6096 | if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| { | |
| 6097 | return fn_proto; | |
| 6098 | } | |
| 6089 | if (ty_node.cast(ast.Node.OptionalType)) |prefix| { | |
| 6090 | if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| { | |
| 6091 | return fn_proto; | |
| 6099 | 6092 | } |
| 6100 | 6093 | } |
| 6101 | 6094 | } |
src-self-hosted/type.zig+17-2| ... | ... | @@ -163,6 +163,22 @@ pub const Type = extern union { |
| 163 | 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 | 182 | .Float, |
| 167 | 183 | .Struct, |
| 168 | 184 | .Optional, |
| ... | ... | @@ -170,14 +186,13 @@ pub const Type = extern union { |
| 170 | 186 | .ErrorSet, |
| 171 | 187 | .Enum, |
| 172 | 188 | .Union, |
| 173 | .Fn, | |
| 174 | 189 | .BoundFn, |
| 175 | 190 | .Opaque, |
| 176 | 191 | .Frame, |
| 177 | 192 | .AnyFrame, |
| 178 | 193 | .Vector, |
| 179 | 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 | } |
| 183 | 198 |
src-self-hosted/value.zig+23-11| ... | ... | @@ -427,8 +427,6 @@ pub const Value = extern union { |
| 427 | 427 | .fn_ccc_void_no_args_type, |
| 428 | 428 | .single_const_pointer_to_comptime_int_type, |
| 429 | 429 | .const_slice_u8_type, |
| 430 | .bool_true, | |
| 431 | .bool_false, | |
| 432 | 430 | .null_value, |
| 433 | 431 | .function, |
| 434 | 432 | .ref_val, |
| ... | ... | @@ -441,8 +439,11 @@ pub const Value = extern union { |
| 441 | 439 | |
| 442 | 440 | .the_one_possible_value, // An integer with one possible value is always zero. |
| 443 | 441 | .zero, |
| 442 | .bool_false, | |
| 444 | 443 | => return BigIntMutable.init(&space.limbs, 0).toConst(), |
| 445 | 444 | |
| 445 | .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(), | |
| 446 | ||
| 446 | 447 | .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(), |
| 447 | 448 | .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(), |
| 448 | 449 | .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(), |
| ... | ... | @@ -493,8 +494,6 @@ pub const Value = extern union { |
| 493 | 494 | .fn_ccc_void_no_args_type, |
| 494 | 495 | .single_const_pointer_to_comptime_int_type, |
| 495 | 496 | .const_slice_u8_type, |
| 496 | .bool_true, | |
| 497 | .bool_false, | |
| 498 | 497 | .null_value, |
| 499 | 498 | .function, |
| 500 | 499 | .ref_val, |
| ... | ... | @@ -507,8 +506,11 @@ pub const Value = extern union { |
| 507 | 506 | |
| 508 | 507 | .zero, |
| 509 | 508 | .the_one_possible_value, // an integer with one possible value is always zero |
| 509 | .bool_false, | |
| 510 | 510 | => return 0, |
| 511 | 511 | |
| 512 | .bool_true => return 1, | |
| 513 | ||
| 512 | 514 | .int_u64 => return self.cast(Payload.Int_u64).?.int, |
| 513 | 515 | .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int), |
| 514 | 516 | .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable, |
| ... | ... | @@ -560,8 +562,6 @@ pub const Value = extern union { |
| 560 | 562 | .fn_ccc_void_no_args_type, |
| 561 | 563 | .single_const_pointer_to_comptime_int_type, |
| 562 | 564 | .const_slice_u8_type, |
| 563 | .bool_true, | |
| 564 | .bool_false, | |
| 565 | 565 | .null_value, |
| 566 | 566 | .function, |
| 567 | 567 | .ref_val, |
| ... | ... | @@ -574,8 +574,11 @@ pub const Value = extern union { |
| 574 | 574 | |
| 575 | 575 | .the_one_possible_value, // an integer with one possible value is always zero |
| 576 | 576 | .zero, |
| 577 | .bool_false, | |
| 577 | 578 | => return 0, |
| 578 | 579 | |
| 580 | .bool_true => return 1, | |
| 581 | ||
| 579 | 582 | .int_u64 => { |
| 580 | 583 | const x = self.cast(Payload.Int_u64).?.int; |
| 581 | 584 | if (x == 0) return 0; |
| ... | ... | @@ -632,8 +635,6 @@ pub const Value = extern union { |
| 632 | 635 | .fn_ccc_void_no_args_type, |
| 633 | 636 | .single_const_pointer_to_comptime_int_type, |
| 634 | 637 | .const_slice_u8_type, |
| 635 | .bool_true, | |
| 636 | .bool_false, | |
| 637 | 638 | .null_value, |
| 638 | 639 | .function, |
| 639 | 640 | .ref_val, |
| ... | ... | @@ -646,8 +647,18 @@ pub const Value = extern union { |
| 646 | 647 | .zero, |
| 647 | 648 | .undef, |
| 648 | 649 | .the_one_possible_value, // an integer with one possible value is always zero |
| 650 | .bool_false, | |
| 649 | 651 | => return true, |
| 650 | 652 | |
| 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 | 662 | .int_u64 => switch (ty.zigTypeTag()) { |
| 652 | 663 | .Int => { |
| 653 | 664 | const x = self.cast(Payload.Int_u64).?.int; |
| ... | ... | @@ -796,8 +807,6 @@ pub const Value = extern union { |
| 796 | 807 | .fn_ccc_void_no_args_type, |
| 797 | 808 | .single_const_pointer_to_comptime_int_type, |
| 798 | 809 | .const_slice_u8_type, |
| 799 | .bool_true, | |
| 800 | .bool_false, | |
| 801 | 810 | .null_value, |
| 802 | 811 | .function, |
| 803 | 812 | .ref_val, |
| ... | ... | @@ -810,8 +819,11 @@ pub const Value = extern union { |
| 810 | 819 | |
| 811 | 820 | .zero, |
| 812 | 821 | .the_one_possible_value, // an integer with one possible value is always zero |
| 822 | .bool_false, | |
| 813 | 823 | => return .eq, |
| 814 | 824 | |
| 825 | .bool_true => return .gt, | |
| 826 | ||
| 815 | 827 | .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), |
| 816 | 828 | .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), |
| 817 | 829 | .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0), |
| ... | ... | @@ -855,7 +867,7 @@ pub const Value = extern union { |
| 855 | 867 | pub fn toBool(self: Value) bool { |
| 856 | 868 | return switch (self.tag()) { |
| 857 | 869 | .bool_true => true, |
| 858 | .bool_false => false, | |
| 870 | .bool_false, .zero => false, | |
| 859 | 871 | else => unreachable, |
| 860 | 872 | }; |
| 861 | 873 | } |
src-self-hosted/zir.zig+30| ... | ... | @@ -56,6 +56,7 @@ pub const Inst = struct { |
| 56 | 56 | declval, |
| 57 | 57 | /// Same as declval but the parameter is a `*Module.Decl` rather than a name. |
| 58 | 58 | declval_in_module, |
| 59 | boolnot, | |
| 59 | 60 | /// String Literal. Makes an anonymous Decl and then takes a pointer to it. |
| 60 | 61 | str, |
| 61 | 62 | int, |
| ... | ... | @@ -115,6 +116,7 @@ pub const Inst = struct { |
| 115 | 116 | .cmp, |
| 116 | 117 | .isnull, |
| 117 | 118 | .isnonnull, |
| 119 | .boolnot, | |
| 118 | 120 | => false, |
| 119 | 121 | |
| 120 | 122 | .condbr, |
| ... | ... | @@ -143,6 +145,7 @@ pub const Inst = struct { |
| 143 | 145 | .declval_in_module => DeclValInModule, |
| 144 | 146 | .compileerror => CompileError, |
| 145 | 147 | .@"const" => Const, |
| 148 | .boolnot => BoolNot, | |
| 146 | 149 | .str => Str, |
| 147 | 150 | .int => Int, |
| 148 | 151 | .inttype => IntType, |
| ... | ... | @@ -299,6 +302,16 @@ pub const Inst = struct { |
| 299 | 302 | kw_args: struct {}, |
| 300 | 303 | }; |
| 301 | 304 | |
| 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 | 315 | pub const Str = struct { |
| 303 | 316 | pub const base_tag = Tag.str; |
| 304 | 317 | base: Inst, |
| ... | ... | @@ -762,6 +775,7 @@ const Writer = struct { |
| 762 | 775 | .declval_in_module => return self.writeInstToStreamGeneric(stream, .declval_in_module, inst), |
| 763 | 776 | .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, inst), |
| 764 | 777 | .@"const" => return self.writeInstToStreamGeneric(stream, .@"const", inst), |
| 778 | .boolnot => return self.writeInstToStreamGeneric(stream, .boolnot, inst), | |
| 765 | 779 | .str => return self.writeInstToStreamGeneric(stream, .str, inst), |
| 766 | 780 | .int => return self.writeInstToStreamGeneric(stream, .int, inst), |
| 767 | 781 | .inttype => return self.writeInstToStreamGeneric(stream, .inttype, inst), |
| ... | ... | @@ -1658,6 +1672,22 @@ const EmitZIR = struct { |
| 1658 | 1672 | }; |
| 1659 | 1673 | for (body.instructions) |inst| { |
| 1660 | 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 | 1691 | .add => blk: { |
| 1662 | 1692 | const old_inst = inst.cast(ir.Inst.Add).?; |
| 1663 | 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 | 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 | } |