| author | |
| committer | |
| log | 0965724e31666d156ca96375eee591380f3c9042 |
| tree | 020b1a51091dfdcef21baeb26e83ca6ee503b9c1 |
| parent | 5f0bde63582c800352b2d11e20bec650bd266a6f |
This makes sense from an organizational point of view, as explained by
this new doc comment at the top of the new file:
//! Semantic analysis of ZIR instructions.
//! This file operates on a `Module` instance, transforming untyped ZIR
//! instructions into semantically-analyzed IR instructions. It does type
//! checking, comptime control flow, and safety-check generation. This is the
//! the heart of the Zig compiler.
//! When deciding if something goes into this file or into Module, here is a
//! guiding principle: if it has to do with (untyped) ZIR instructions, it goes
//! here. If the analysis operates on typed IR instructions, it goes in Module.
Before:
4009 src-self-hosted/Module.zig
After:
2776 src-self-hosted/Module.zig
1128 src-self-hosted/zir_sema.zig
This should be sufficient to avoid the situation we have in stage1 where
ir.cpp is 32,516 lines.3 files changed, 1364 insertions(+), 1351 deletions(-)
src-self-hosted/Module.zig+52-1285| ... | ... | @@ -20,6 +20,7 @@ const ast = std.zig.ast; |
| 20 | 20 | const trace = @import("tracy.zig").trace; |
| 21 | 21 | const liveness = @import("liveness.zig"); |
| 22 | 22 | const astgen = @import("astgen.zig"); |
| 23 | const zir_sema = @import("zir_sema.zig"); | |
| 23 | 24 | |
| 24 | 25 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 25 | 26 | gpa: *Allocator, |
| ... | ... | @@ -246,7 +247,7 @@ pub const Decl = struct { |
| 246 | 247 | std.debug.warn("\n", .{}); |
| 247 | 248 | } |
| 248 | 249 | |
| 249 | fn typedValueManaged(self: *Decl) ?*TypedValue.Managed { | |
| 250 | pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed { | |
| 250 | 251 | switch (self.typed_value) { |
| 251 | 252 | .most_recent => |*x| return x, |
| 252 | 253 | .never_succeeded => return null, |
| ... | ... | @@ -1085,7 +1086,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 1085 | 1086 | }; |
| 1086 | 1087 | } |
| 1087 | 1088 | |
| 1088 | fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { | |
| 1089 | pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { | |
| 1089 | 1090 | const tracy = trace(@src()); |
| 1090 | 1091 | defer tracy.end(); |
| 1091 | 1092 | |
| ... | ... | @@ -1129,7 +1130,7 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { |
| 1129 | 1130 | }; |
| 1130 | 1131 | |
| 1131 | 1132 | const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module| |
| 1132 | try self.analyzeZirDecl(decl, zir_module.contents.module.decls[decl.src_index]) | |
| 1133 | try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index]) | |
| 1133 | 1134 | else |
| 1134 | 1135 | self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { |
| 1135 | 1136 | error.OutOfMemory => return error.OutOfMemory, |
| ... | ... | @@ -1205,7 +1206,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1205 | 1206 | const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len); |
| 1206 | 1207 | |
| 1207 | 1208 | const fn_src = tree.token_locs[fn_proto.fn_token].start; |
| 1208 | const type_type = try self.addZIRInstConst(&fn_type_scope.base, fn_src, .{ | |
| 1209 | const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ | |
| 1209 | 1210 | .ty = Type.initTag(.type), |
| 1210 | 1211 | .val = Value.initTag(.type_type), |
| 1211 | 1212 | }); |
| ... | ... | @@ -1244,11 +1245,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1244 | 1245 | }; |
| 1245 | 1246 | |
| 1246 | 1247 | const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr); |
| 1247 | const fn_type_inst = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.FnType, .{ | |
| 1248 | const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ | |
| 1248 | 1249 | .return_type = return_type_inst, |
| 1249 | 1250 | .param_types = param_types, |
| 1250 | 1251 | }, .{}); |
| 1251 | _ = try self.addZIRUnOp(&fn_type_scope.base, fn_src, .@"return", fn_type_inst); | |
| 1252 | _ = try astgen.addZIRUnOp(self, &fn_type_scope.base, fn_src, .@"return", fn_type_inst); | |
| 1252 | 1253 | |
| 1253 | 1254 | // We need the memory for the Type to go into the arena for the Decl |
| 1254 | 1255 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); |
| ... | ... | @@ -1264,7 +1265,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1264 | 1265 | }; |
| 1265 | 1266 | defer block_scope.instructions.deinit(self.gpa); |
| 1266 | 1267 | |
| 1267 | const fn_type = try self.analyzeBodyValueAsType(&block_scope, .{ | |
| 1268 | const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, .{ | |
| 1268 | 1269 | .instructions = fn_type_scope.instructions.items, |
| 1269 | 1270 | }); |
| 1270 | 1271 | const new_func = try decl_arena.allocator.create(Fn); |
| ... | ... | @@ -1317,7 +1318,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1317 | 1318 | !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())) |
| 1318 | 1319 | { |
| 1319 | 1320 | const src = tree.token_locs[body_block.rbrace].start; |
| 1320 | _ = try self.addZIRNoOp(&gen_scope.base, src, .returnvoid); | |
| 1321 | _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); | |
| 1321 | 1322 | } |
| 1322 | 1323 | |
| 1323 | 1324 | const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR); |
| ... | ... | @@ -1387,19 +1388,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1387 | 1388 | } |
| 1388 | 1389 | } |
| 1389 | 1390 | |
| 1390 | fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type { | |
| 1391 | try self.analyzeBody(&block_scope.base, body); | |
| 1392 | for (block_scope.instructions.items) |inst| { | |
| 1393 | if (inst.castTag(.ret)) |ret| { | |
| 1394 | const val = try self.resolveConstValue(&block_scope.base, ret.operand); | |
| 1395 | return val.toType(); | |
| 1396 | } else { | |
| 1397 | return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{}); | |
| 1398 | } | |
| 1399 | } | |
| 1400 | unreachable; | |
| 1401 | } | |
| 1402 | ||
| 1403 | 1391 | fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { |
| 1404 | 1392 | try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1); |
| 1405 | 1393 | try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1); |
| ... | ... | @@ -1600,7 +1588,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 1600 | 1588 | } |
| 1601 | 1589 | } |
| 1602 | 1590 | for (exports_to_resolve.items) |export_decl| { |
| 1603 | _ = try self.resolveZirDecl(&root_scope.base, export_decl); | |
| 1591 | _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl); | |
| 1604 | 1592 | } |
| 1605 | 1593 | // Handle explicitly deleted decls from the source code. Not to be confused |
| 1606 | 1594 | // with when we delete decls because they are no longer referenced. |
| ... | ... | @@ -1705,7 +1693,7 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { |
| 1705 | 1693 | func.analysis = .{ .in_progress = {} }; |
| 1706 | 1694 | //std.debug.warn("set {} to in_progress\n", .{decl.name}); |
| 1707 | 1695 | |
| 1708 | try self.analyzeBody(&inner_block.base, fn_zir.body); | |
| 1696 | try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body); | |
| 1709 | 1697 | |
| 1710 | 1698 | const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); |
| 1711 | 1699 | func.analysis = .{ .success = .{ .instructions = instructions } }; |
| ... | ... | @@ -1758,131 +1746,18 @@ fn createNewDecl( |
| 1758 | 1746 | return new_decl; |
| 1759 | 1747 | } |
| 1760 | 1748 | |
| 1761 | fn analyzeZirDecl(self: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool { | |
| 1762 | var decl_scope: Scope.DeclAnalysis = .{ | |
| 1763 | .decl = decl, | |
| 1764 | .arena = std.heap.ArenaAllocator.init(self.gpa), | |
| 1765 | }; | |
| 1766 | errdefer decl_scope.arena.deinit(); | |
| 1767 | ||
| 1768 | decl.analysis = .in_progress; | |
| 1769 | ||
| 1770 | const typed_value = try self.analyzeConstInst(&decl_scope.base, src_decl.inst); | |
| 1771 | const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); | |
| 1772 | ||
| 1773 | var prev_type_has_bits = false; | |
| 1774 | var type_changed = true; | |
| 1775 | ||
| 1776 | if (decl.typedValueManaged()) |tvm| { | |
| 1777 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); | |
| 1778 | type_changed = !tvm.typed_value.ty.eql(typed_value.ty); | |
| 1779 | ||
| 1780 | tvm.deinit(self.gpa); | |
| 1781 | } | |
| 1782 | ||
| 1783 | arena_state.* = decl_scope.arena.state; | |
| 1784 | decl.typed_value = .{ | |
| 1785 | .most_recent = .{ | |
| 1786 | .typed_value = typed_value, | |
| 1787 | .arena = arena_state, | |
| 1788 | }, | |
| 1789 | }; | |
| 1790 | decl.analysis = .complete; | |
| 1791 | decl.generation = self.generation; | |
| 1792 | if (typed_value.ty.hasCodeGenBits()) { | |
| 1793 | // We don't fully codegen the decl until later, but we do need to reserve a global | |
| 1794 | // offset table index for it. This allows us to codegen decls out of dependency order, | |
| 1795 | // increasing how many computations can be done in parallel. | |
| 1796 | try self.bin_file.allocateDeclIndexes(decl); | |
| 1797 | try self.work_queue.writeItem(.{ .codegen_decl = decl }); | |
| 1798 | } else if (prev_type_has_bits) { | |
| 1799 | self.bin_file.freeDecl(decl); | |
| 1800 | } | |
| 1801 | ||
| 1802 | return type_changed; | |
| 1803 | } | |
| 1804 | ||
| 1805 | fn resolveZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl { | |
| 1806 | const zir_module = self.root_scope.cast(Scope.ZIRModule).?; | |
| 1807 | const entry = zir_module.contents.module.findDecl(src_decl.name).?; | |
| 1808 | return self.resolveZirDeclHavingIndex(scope, src_decl, entry.index); | |
| 1809 | } | |
| 1810 | ||
| 1811 | fn resolveZirDeclHavingIndex(self: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl { | |
| 1812 | const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name); | |
| 1813 | const decl = self.decl_table.get(name_hash).?; | |
| 1814 | decl.src_index = src_index; | |
| 1815 | try self.ensureDeclAnalyzed(decl); | |
| 1816 | return decl; | |
| 1817 | } | |
| 1818 | ||
| 1819 | /// Declares a dependency on the decl. | |
| 1820 | fn resolveCompleteZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl { | |
| 1821 | const decl = try self.resolveZirDecl(scope, src_decl); | |
| 1822 | switch (decl.analysis) { | |
| 1823 | .unreferenced => unreachable, | |
| 1824 | .in_progress => unreachable, | |
| 1825 | .outdated => unreachable, | |
| 1826 | ||
| 1827 | .dependency_failure, | |
| 1828 | .sema_failure, | |
| 1829 | .sema_failure_retryable, | |
| 1830 | .codegen_failure, | |
| 1831 | .codegen_failure_retryable, | |
| 1832 | => return error.AnalysisFail, | |
| 1833 | ||
| 1834 | .complete => {}, | |
| 1835 | } | |
| 1836 | return decl; | |
| 1837 | } | |
| 1838 | ||
| 1839 | /// TODO Look into removing this function. The body is only needed for .zir files, not .zig files. | |
| 1840 | fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { | |
| 1841 | if (old_inst.analyzed_inst) |inst| return inst; | |
| 1842 | ||
| 1843 | // If this assert trips, the instruction that was referenced did not get properly | |
| 1844 | // analyzed before it was referenced. | |
| 1845 | const zir_module = scope.namespace().cast(Scope.ZIRModule).?; | |
| 1846 | const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: { | |
| 1847 | const decl_name = declval.positionals.name; | |
| 1848 | const entry = zir_module.contents.module.findDecl(decl_name) orelse | |
| 1849 | return self.fail(scope, old_inst.src, "decl '{}' not found", .{decl_name}); | |
| 1850 | break :blk entry; | |
| 1851 | } else blk: { | |
| 1852 | // If this assert trips, the instruction that was referenced did not get | |
| 1853 | // properly analyzed by a previous instruction analysis before it was | |
| 1854 | // referenced by the current one. | |
| 1855 | break :blk zir_module.contents.module.findInstDecl(old_inst).?; | |
| 1856 | }; | |
| 1857 | const decl = try self.resolveCompleteZirDecl(scope, entry.decl); | |
| 1858 | const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl); | |
| 1859 | // Note: it would be tempting here to store the result into old_inst.analyzed_inst field, | |
| 1860 | // but this would prevent the analyzeDeclRef from happening, which is needed to properly | |
| 1861 | // detect Decl dependencies and dependency failures on updates. | |
| 1862 | return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src); | |
| 1863 | } | |
| 1864 | ||
| 1865 | 1749 | /// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites. |
| 1866 | fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { | |
| 1750 | pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { | |
| 1867 | 1751 | return scope.cast(Scope.Block) orelse |
| 1868 | 1752 | return self.fail(scope, src, "instruction illegal outside function body", .{}); |
| 1869 | 1753 | } |
| 1870 | 1754 | |
| 1871 | fn resolveInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue { | |
| 1872 | const new_inst = try self.resolveInst(scope, old_inst); | |
| 1873 | const val = try self.resolveConstValue(scope, new_inst); | |
| 1874 | return TypedValue{ | |
| 1875 | .ty = new_inst.ty, | |
| 1876 | .val = val, | |
| 1877 | }; | |
| 1878 | } | |
| 1879 | ||
| 1880 | fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value { | |
| 1755 | pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value { | |
| 1881 | 1756 | return (try self.resolveDefinedValue(scope, base)) orelse |
| 1882 | 1757 | return self.fail(scope, base.src, "unable to resolve comptime value", .{}); |
| 1883 | 1758 | } |
| 1884 | 1759 | |
| 1885 | fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value { | |
| 1760 | pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value { | |
| 1886 | 1761 | if (base.value()) |val| { |
| 1887 | 1762 | if (val.isUndef()) { |
| 1888 | 1763 | return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{}); |
| ... | ... | @@ -1892,23 +1767,7 @@ fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value { |
| 1892 | 1767 | return null; |
| 1893 | 1768 | } |
| 1894 | 1769 | |
| 1895 | fn resolveConstString(self: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 { | |
| 1896 | const new_inst = try self.resolveInst(scope, old_inst); | |
| 1897 | const wanted_type = Type.initTag(.const_slice_u8); | |
| 1898 | const coerced_inst = try self.coerce(scope, wanted_type, new_inst); | |
| 1899 | const val = try self.resolveConstValue(scope, coerced_inst); | |
| 1900 | return val.toAllocatedBytes(scope.arena()); | |
| 1901 | } | |
| 1902 | ||
| 1903 | fn resolveType(self: *Module, scope: *Scope, old_inst: *zir.Inst) !Type { | |
| 1904 | const new_inst = try self.resolveInst(scope, old_inst); | |
| 1905 | const wanted_type = Type.initTag(.@"type"); | |
| 1906 | const coerced_inst = try self.coerce(scope, wanted_type, new_inst); | |
| 1907 | const val = try self.resolveConstValue(scope, coerced_inst); | |
| 1908 | return val.toType(); | |
| 1909 | } | |
| 1910 | ||
| 1911 | fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const u8, exported_decl: *Decl) !void { | |
| 1770 | pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const u8, exported_decl: *Decl) !void { | |
| 1912 | 1771 | try self.ensureDeclAnalyzed(exported_decl); |
| 1913 | 1772 | const typed_value = exported_decl.typed_value.most_recent.typed_value; |
| 1914 | 1773 | switch (typed_value.ty.zigTypeTag()) { |
| ... | ... | @@ -1980,7 +1839,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const |
| 1980 | 1839 | }; |
| 1981 | 1840 | } |
| 1982 | 1841 | |
| 1983 | fn addNoOp( | |
| 1842 | pub fn addNoOp( | |
| 1984 | 1843 | self: *Module, |
| 1985 | 1844 | block: *Scope.Block, |
| 1986 | 1845 | src: usize, |
| ... | ... | @@ -1999,7 +1858,7 @@ fn addNoOp( |
| 1999 | 1858 | return &inst.base; |
| 2000 | 1859 | } |
| 2001 | 1860 | |
| 2002 | fn addUnOp( | |
| 1861 | pub fn addUnOp( | |
| 2003 | 1862 | self: *Module, |
| 2004 | 1863 | block: *Scope.Block, |
| 2005 | 1864 | src: usize, |
| ... | ... | @@ -2020,7 +1879,7 @@ fn addUnOp( |
| 2020 | 1879 | return &inst.base; |
| 2021 | 1880 | } |
| 2022 | 1881 | |
| 2023 | fn addBinOp( | |
| 1882 | pub fn addBinOp( | |
| 2024 | 1883 | self: *Module, |
| 2025 | 1884 | block: *Scope.Block, |
| 2026 | 1885 | src: usize, |
| ... | ... | @@ -2043,7 +1902,7 @@ fn addBinOp( |
| 2043 | 1902 | return &inst.base; |
| 2044 | 1903 | } |
| 2045 | 1904 | |
| 2046 | fn addBr( | |
| 1905 | pub fn addBr( | |
| 2047 | 1906 | self: *Module, |
| 2048 | 1907 | scope_block: *Scope.Block, |
| 2049 | 1908 | src: usize, |
| ... | ... | @@ -2064,7 +1923,7 @@ fn addBr( |
| 2064 | 1923 | return &inst.base; |
| 2065 | 1924 | } |
| 2066 | 1925 | |
| 2067 | fn addCondBr( | |
| 1926 | pub fn addCondBr( | |
| 2068 | 1927 | self: *Module, |
| 2069 | 1928 | block: *Scope.Block, |
| 2070 | 1929 | src: usize, |
| ... | ... | @@ -2087,7 +1946,7 @@ fn addCondBr( |
| 2087 | 1946 | return &inst.base; |
| 2088 | 1947 | } |
| 2089 | 1948 | |
| 2090 | fn addCall( | |
| 1949 | pub fn addCall( | |
| 2091 | 1950 | self: *Module, |
| 2092 | 1951 | block: *Scope.Block, |
| 2093 | 1952 | src: usize, |
| ... | ... | @@ -2109,138 +1968,7 @@ fn addCall( |
| 2109 | 1968 | return &inst.base; |
| 2110 | 1969 | } |
| 2111 | 1970 | |
| 2112 | pub fn addZIRInstSpecial( | |
| 2113 | self: *Module, | |
| 2114 | scope: *Scope, | |
| 2115 | src: usize, | |
| 2116 | comptime T: type, | |
| 2117 | positionals: std.meta.fieldInfo(T, "positionals").field_type, | |
| 2118 | kw_args: std.meta.fieldInfo(T, "kw_args").field_type, | |
| 2119 | ) !*T { | |
| 2120 | const gen_zir = scope.getGenZIR(); | |
| 2121 | try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1); | |
| 2122 | const inst = try gen_zir.arena.create(T); | |
| 2123 | inst.* = .{ | |
| 2124 | .base = .{ | |
| 2125 | .tag = T.base_tag, | |
| 2126 | .src = src, | |
| 2127 | }, | |
| 2128 | .positionals = positionals, | |
| 2129 | .kw_args = kw_args, | |
| 2130 | }; | |
| 2131 | gen_zir.instructions.appendAssumeCapacity(&inst.base); | |
| 2132 | return inst; | |
| 2133 | } | |
| 2134 | ||
| 2135 | pub fn addZIRNoOpT(self: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp { | |
| 2136 | const gen_zir = scope.getGenZIR(); | |
| 2137 | try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1); | |
| 2138 | const inst = try gen_zir.arena.create(zir.Inst.NoOp); | |
| 2139 | inst.* = .{ | |
| 2140 | .base = .{ | |
| 2141 | .tag = tag, | |
| 2142 | .src = src, | |
| 2143 | }, | |
| 2144 | .positionals = .{}, | |
| 2145 | .kw_args = .{}, | |
| 2146 | }; | |
| 2147 | gen_zir.instructions.appendAssumeCapacity(&inst.base); | |
| 2148 | return inst; | |
| 2149 | } | |
| 2150 | ||
| 2151 | pub fn addZIRNoOp(self: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst { | |
| 2152 | const inst = try self.addZIRNoOpT(scope, src, tag); | |
| 2153 | return &inst.base; | |
| 2154 | } | |
| 2155 | ||
| 2156 | pub fn addZIRUnOp( | |
| 2157 | self: *Module, | |
| 2158 | scope: *Scope, | |
| 2159 | src: usize, | |
| 2160 | tag: zir.Inst.Tag, | |
| 2161 | operand: *zir.Inst, | |
| 2162 | ) !*zir.Inst { | |
| 2163 | const gen_zir = scope.getGenZIR(); | |
| 2164 | try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1); | |
| 2165 | const inst = try gen_zir.arena.create(zir.Inst.UnOp); | |
| 2166 | inst.* = .{ | |
| 2167 | .base = .{ | |
| 2168 | .tag = tag, | |
| 2169 | .src = src, | |
| 2170 | }, | |
| 2171 | .positionals = .{ | |
| 2172 | .operand = operand, | |
| 2173 | }, | |
| 2174 | .kw_args = .{}, | |
| 2175 | }; | |
| 2176 | gen_zir.instructions.appendAssumeCapacity(&inst.base); | |
| 2177 | return &inst.base; | |
| 2178 | } | |
| 2179 | ||
| 2180 | pub fn addZIRBinOp( | |
| 2181 | self: *Module, | |
| 2182 | scope: *Scope, | |
| 2183 | src: usize, | |
| 2184 | tag: zir.Inst.Tag, | |
| 2185 | lhs: *zir.Inst, | |
| 2186 | rhs: *zir.Inst, | |
| 2187 | ) !*zir.Inst { | |
| 2188 | const gen_zir = scope.getGenZIR(); | |
| 2189 | try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1); | |
| 2190 | const inst = try gen_zir.arena.create(zir.Inst.BinOp); | |
| 2191 | inst.* = .{ | |
| 2192 | .base = .{ | |
| 2193 | .tag = tag, | |
| 2194 | .src = src, | |
| 2195 | }, | |
| 2196 | .positionals = .{ | |
| 2197 | .lhs = lhs, | |
| 2198 | .rhs = rhs, | |
| 2199 | }, | |
| 2200 | .kw_args = .{}, | |
| 2201 | }; | |
| 2202 | gen_zir.instructions.appendAssumeCapacity(&inst.base); | |
| 2203 | return &inst.base; | |
| 2204 | } | |
| 2205 | ||
| 2206 | pub fn addZIRInst( | |
| 2207 | self: *Module, | |
| 2208 | scope: *Scope, | |
| 2209 | src: usize, | |
| 2210 | comptime T: type, | |
| 2211 | positionals: std.meta.fieldInfo(T, "positionals").field_type, | |
| 2212 | kw_args: std.meta.fieldInfo(T, "kw_args").field_type, | |
| 2213 | ) !*zir.Inst { | |
| 2214 | const inst_special = try self.addZIRInstSpecial(scope, src, T, positionals, kw_args); | |
| 2215 | return &inst_special.base; | |
| 2216 | } | |
| 2217 | ||
| 2218 | /// TODO The existence of this function is a workaround for a bug in stage1. | |
| 2219 | pub fn addZIRInstConst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst { | |
| 2220 | const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type; | |
| 2221 | return self.addZIRInst(scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{}); | |
| 2222 | } | |
| 2223 | ||
| 2224 | /// TODO The existence of this function is a workaround for a bug in stage1. | |
| 2225 | pub fn addZIRInstBlock(self: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block { | |
| 2226 | const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type; | |
| 2227 | return self.addZIRInstSpecial(scope, src, zir.Inst.Block, P{ .body = body }, .{}); | |
| 2228 | } | |
| 2229 | ||
| 2230 | fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T { | |
| 2231 | const inst = try block.arena.create(T); | |
| 2232 | inst.* = .{ | |
| 2233 | .base = .{ | |
| 2234 | .tag = T.base_tag, | |
| 2235 | .ty = ty, | |
| 2236 | .src = src, | |
| 2237 | }, | |
| 2238 | }; | |
| 2239 | try block.instructions.append(self.gpa, &inst.base); | |
| 2240 | return inst; | |
| 2241 | } | |
| 2242 | ||
| 2243 | fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst { | |
| 1971 | pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst { | |
| 2244 | 1972 | const const_inst = try scope.arena().create(Inst.Constant); |
| 2245 | 1973 | const_inst.* = .{ |
| 2246 | 1974 | .base = .{ |
| ... | ... | @@ -2253,42 +1981,42 @@ fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) |
| 2253 | 1981 | return &const_inst.base; |
| 2254 | 1982 | } |
| 2255 | 1983 | |
| 2256 | fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | |
| 1984 | pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | |
| 2257 | 1985 | return self.constInst(scope, src, .{ |
| 2258 | 1986 | .ty = Type.initTag(.type), |
| 2259 | 1987 | .val = try ty.toValue(scope.arena()), |
| 2260 | 1988 | }); |
| 2261 | 1989 | } |
| 2262 | 1990 | |
| 2263 | fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst { | |
| 1991 | pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst { | |
| 2264 | 1992 | return self.constInst(scope, src, .{ |
| 2265 | 1993 | .ty = Type.initTag(.void), |
| 2266 | 1994 | .val = Value.initTag(.the_one_possible_value), |
| 2267 | 1995 | }); |
| 2268 | 1996 | } |
| 2269 | 1997 | |
| 2270 | fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst { | |
| 1998 | pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst { | |
| 2271 | 1999 | return self.constInst(scope, src, .{ |
| 2272 | 2000 | .ty = Type.initTag(.noreturn), |
| 2273 | 2001 | .val = Value.initTag(.the_one_possible_value), |
| 2274 | 2002 | }); |
| 2275 | 2003 | } |
| 2276 | 2004 | |
| 2277 | fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | |
| 2005 | pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | |
| 2278 | 2006 | return self.constInst(scope, src, .{ |
| 2279 | 2007 | .ty = ty, |
| 2280 | 2008 | .val = Value.initTag(.undef), |
| 2281 | 2009 | }); |
| 2282 | 2010 | } |
| 2283 | 2011 | |
| 2284 | fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst { | |
| 2012 | pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst { | |
| 2285 | 2013 | return self.constInst(scope, src, .{ |
| 2286 | 2014 | .ty = Type.initTag(.bool), |
| 2287 | 2015 | .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)], |
| 2288 | 2016 | }); |
| 2289 | 2017 | } |
| 2290 | 2018 | |
| 2291 | fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst { | |
| 2019 | pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst { | |
| 2292 | 2020 | const int_payload = try scope.arena().create(Value.Payload.Int_u64); |
| 2293 | 2021 | int_payload.* = .{ .int = int }; |
| 2294 | 2022 | |
| ... | ... | @@ -2298,7 +2026,7 @@ fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64 |
| 2298 | 2026 | }); |
| 2299 | 2027 | } |
| 2300 | 2028 | |
| 2301 | fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst { | |
| 2029 | pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst { | |
| 2302 | 2030 | const int_payload = try scope.arena().create(Value.Payload.Int_i64); |
| 2303 | 2031 | int_payload.* = .{ .int = int }; |
| 2304 | 2032 | |
| ... | ... | @@ -2308,7 +2036,7 @@ fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) |
| 2308 | 2036 | }); |
| 2309 | 2037 | } |
| 2310 | 2038 | |
| 2311 | fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst { | |
| 2039 | pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst { | |
| 2312 | 2040 | const val_payload = if (big_int.positive) blk: { |
| 2313 | 2041 | if (big_int.to(u64)) |x| { |
| 2314 | 2042 | return self.constIntUnsigned(scope, src, ty, x); |
| ... | ... | @@ -2337,217 +2065,7 @@ fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigI |
| 2337 | 2065 | }); |
| 2338 | 2066 | } |
| 2339 | 2067 | |
| 2340 | fn analyzeConstInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue { | |
| 2341 | const new_inst = try self.analyzeInst(scope, old_inst); | |
| 2342 | return TypedValue{ | |
| 2343 | .ty = new_inst.ty, | |
| 2344 | .val = try self.resolveConstValue(scope, new_inst), | |
| 2345 | }; | |
| 2346 | } | |
| 2347 | ||
| 2348 | fn analyzeInstConst(self: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst { | |
| 2349 | // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions | |
| 2350 | // after analysis. | |
| 2351 | const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena()); | |
| 2352 | return self.constInst(scope, const_inst.base.src, typed_value_copy); | |
| 2353 | } | |
| 2354 | ||
| 2355 | fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { | |
| 2356 | switch (old_inst.tag) { | |
| 2357 | .alloc => return self.analyzeInstAlloc(scope, old_inst.castTag(.alloc).?), | |
| 2358 | .alloc_inferred => return self.analyzeInstAllocInferred(scope, old_inst.castTag(.alloc_inferred).?), | |
| 2359 | .arg => return self.analyzeInstArg(scope, old_inst.castTag(.arg).?), | |
| 2360 | .bitcast_lvalue => return self.analyzeInstBitCastLValue(scope, old_inst.castTag(.bitcast_lvalue).?), | |
| 2361 | .bitcast_result_ptr => return self.analyzeInstBitCastResultPtr(scope, old_inst.castTag(.bitcast_result_ptr).?), | |
| 2362 | .block => return self.analyzeInstBlock(scope, old_inst.castTag(.block).?), | |
| 2363 | .@"break" => return self.analyzeInstBreak(scope, old_inst.castTag(.@"break").?), | |
| 2364 | .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.castTag(.breakpoint).?), | |
| 2365 | .breakvoid => return self.analyzeInstBreakVoid(scope, old_inst.castTag(.breakvoid).?), | |
| 2366 | .call => return self.analyzeInstCall(scope, old_inst.castTag(.call).?), | |
| 2367 | .coerce_result_block_ptr => return self.analyzeInstCoerceResultBlockPtr(scope, old_inst.castTag(.coerce_result_block_ptr).?), | |
| 2368 | .coerce_result_ptr => return self.analyzeInstCoerceResultPtr(scope, old_inst.castTag(.coerce_result_ptr).?), | |
| 2369 | .coerce_to_ptr_elem => return self.analyzeInstCoerceToPtrElem(scope, old_inst.castTag(.coerce_to_ptr_elem).?), | |
| 2370 | .compileerror => return self.analyzeInstCompileError(scope, old_inst.castTag(.compileerror).?), | |
| 2371 | .@"const" => return self.analyzeInstConst(scope, old_inst.castTag(.@"const").?), | |
| 2372 | .declref => return self.analyzeInstDeclRef(scope, old_inst.castTag(.declref).?), | |
| 2373 | .declref_str => return self.analyzeInstDeclRefStr(scope, old_inst.castTag(.declref_str).?), | |
| 2374 | .declval => return self.analyzeInstDeclVal(scope, old_inst.castTag(.declval).?), | |
| 2375 | .declval_in_module => return self.analyzeInstDeclValInModule(scope, old_inst.castTag(.declval_in_module).?), | |
| 2376 | .ensure_result_used => return self.analyzeInstEnsureResultUsed(scope, old_inst.castTag(.ensure_result_used).?), | |
| 2377 | .ensure_result_non_error => return self.analyzeInstEnsureResultNonError(scope, old_inst.castTag(.ensure_result_non_error).?), | |
| 2378 | .ref => return self.analyzeInstRef(scope, old_inst.castTag(.ref).?), | |
| 2379 | .ret_ptr => return self.analyzeInstRetPtr(scope, old_inst.castTag(.ret_ptr).?), | |
| 2380 | .ret_type => return self.analyzeInstRetType(scope, old_inst.castTag(.ret_type).?), | |
| 2381 | .store => return self.analyzeInstStore(scope, old_inst.castTag(.store).?), | |
| 2382 | .str => return self.analyzeInstStr(scope, old_inst.castTag(.str).?), | |
| 2383 | .int => { | |
| 2384 | const big_int = old_inst.castTag(.int).?.positionals.int; | |
| 2385 | return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int); | |
| 2386 | }, | |
| 2387 | .inttype => return self.analyzeInstIntType(scope, old_inst.castTag(.inttype).?), | |
| 2388 | .param_type => return self.analyzeInstParamType(scope, old_inst.castTag(.param_type).?), | |
| 2389 | .ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.castTag(.ptrtoint).?), | |
| 2390 | .fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.castTag(.fieldptr).?), | |
| 2391 | .deref => return self.analyzeInstDeref(scope, old_inst.castTag(.deref).?), | |
| 2392 | .as => return self.analyzeInstAs(scope, old_inst.castTag(.as).?), | |
| 2393 | .@"asm" => return self.analyzeInstAsm(scope, old_inst.castTag(.@"asm").?), | |
| 2394 | .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.castTag(.@"unreachable").?), | |
| 2395 | .unreach_nocheck => return self.analyzeInstUnreachNoChk(scope, old_inst.castTag(.unreach_nocheck).?), | |
| 2396 | .@"return" => return self.analyzeInstRet(scope, old_inst.castTag(.@"return").?), | |
| 2397 | .returnvoid => return self.analyzeInstRetVoid(scope, old_inst.castTag(.returnvoid).?), | |
| 2398 | .@"fn" => return self.analyzeInstFn(scope, old_inst.castTag(.@"fn").?), | |
| 2399 | .@"export" => return self.analyzeInstExport(scope, old_inst.castTag(.@"export").?), | |
| 2400 | .primitive => return self.analyzeInstPrimitive(scope, old_inst.castTag(.primitive).?), | |
| 2401 | .fntype => return self.analyzeInstFnType(scope, old_inst.castTag(.fntype).?), | |
| 2402 | .intcast => return self.analyzeInstIntCast(scope, old_inst.castTag(.intcast).?), | |
| 2403 | .bitcast => return self.analyzeInstBitCast(scope, old_inst.castTag(.bitcast).?), | |
| 2404 | .floatcast => return self.analyzeInstFloatCast(scope, old_inst.castTag(.floatcast).?), | |
| 2405 | .elemptr => return self.analyzeInstElemPtr(scope, old_inst.castTag(.elemptr).?), | |
| 2406 | .add => return self.analyzeInstArithmetic(scope, old_inst.castTag(.add).?), | |
| 2407 | .addwrap => return self.analyzeInstArithmetic(scope, old_inst.castTag(.addwrap).?), | |
| 2408 | .sub => return self.analyzeInstArithmetic(scope, old_inst.castTag(.sub).?), | |
| 2409 | .subwrap => return self.analyzeInstArithmetic(scope, old_inst.castTag(.subwrap).?), | |
| 2410 | .mul => return self.analyzeInstArithmetic(scope, old_inst.castTag(.mul).?), | |
| 2411 | .mulwrap => return self.analyzeInstArithmetic(scope, old_inst.castTag(.mulwrap).?), | |
| 2412 | .div => return self.analyzeInstArithmetic(scope, old_inst.castTag(.div).?), | |
| 2413 | .mod_rem => return self.analyzeInstArithmetic(scope, old_inst.castTag(.mod_rem).?), | |
| 2414 | .array_cat => return self.analyzeInstArrayCat(scope, old_inst.castTag(.array_cat).?), | |
| 2415 | .array_mul => return self.analyzeInstArrayMul(scope, old_inst.castTag(.array_mul).?), | |
| 2416 | .bitand => return self.analyzeInstBitwise(scope, old_inst.castTag(.bitand).?), | |
| 2417 | .bitor => return self.analyzeInstBitwise(scope, old_inst.castTag(.bitor).?), | |
| 2418 | .xor => return self.analyzeInstBitwise(scope, old_inst.castTag(.xor).?), | |
| 2419 | .shl => return self.analyzeInstShl(scope, old_inst.castTag(.shl).?), | |
| 2420 | .shr => return self.analyzeInstShr(scope, old_inst.castTag(.shr).?), | |
| 2421 | .cmp_lt => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lt).?, .lt), | |
| 2422 | .cmp_lte => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_lte).?, .lte), | |
| 2423 | .cmp_eq => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_eq).?, .eq), | |
| 2424 | .cmp_gte => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_gte).?, .gte), | |
| 2425 | .cmp_gt => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_gt).?, .gt), | |
| 2426 | .cmp_neq => return self.analyzeInstCmp(scope, old_inst.castTag(.cmp_neq).?, .neq), | |
| 2427 | .condbr => return self.analyzeInstCondBr(scope, old_inst.castTag(.condbr).?), | |
| 2428 | .isnull => return self.analyzeInstIsNonNull(scope, old_inst.castTag(.isnull).?, true), | |
| 2429 | .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.castTag(.isnonnull).?, false), | |
| 2430 | .boolnot => return self.analyzeInstBoolNot(scope, old_inst.castTag(.boolnot).?), | |
| 2431 | .typeof => return self.analyzeInstTypeOf(scope, old_inst.castTag(.typeof).?), | |
| 2432 | } | |
| 2433 | } | |
| 2434 | ||
| 2435 | fn analyzeInstCoerceResultBlockPtr( | |
| 2436 | self: *Module, | |
| 2437 | scope: *Scope, | |
| 2438 | inst: *zir.Inst.CoerceResultBlockPtr, | |
| 2439 | ) InnerError!*Inst { | |
| 2440 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{}); | |
| 2441 | } | |
| 2442 | ||
| 2443 | fn analyzeInstBitCastLValue(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2444 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastLValue", .{}); | |
| 2445 | } | |
| 2446 | ||
| 2447 | fn analyzeInstBitCastResultPtr(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2448 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{}); | |
| 2449 | } | |
| 2450 | ||
| 2451 | fn analyzeInstCoerceResultPtr(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 2452 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{}); | |
| 2453 | } | |
| 2454 | ||
| 2455 | fn analyzeInstCoerceToPtrElem(self: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst { | |
| 2456 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceToPtrElem", .{}); | |
| 2457 | } | |
| 2458 | ||
| 2459 | fn analyzeInstRetPtr(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 2460 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstRetPtr", .{}); | |
| 2461 | } | |
| 2462 | ||
| 2463 | fn analyzeInstRef(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2464 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstRef", .{}); | |
| 2465 | } | |
| 2466 | ||
| 2467 | fn analyzeInstRetType(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 2468 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 2469 | const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | |
| 2470 | const ret_type = fn_ty.fnReturnType(); | |
| 2471 | return self.constType(scope, inst.base.src, ret_type); | |
| 2472 | } | |
| 2473 | ||
| 2474 | fn analyzeInstEnsureResultUsed(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2475 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 2476 | switch (operand.ty.zigTypeTag()) { | |
| 2477 | .Void, .NoReturn => return self.constVoid(scope, operand.src), | |
| 2478 | else => return self.fail(scope, operand.src, "expression value is ignored", .{}), | |
| 2479 | } | |
| 2480 | } | |
| 2481 | ||
| 2482 | fn analyzeInstEnsureResultNonError(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2483 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 2484 | switch (operand.ty.zigTypeTag()) { | |
| 2485 | .ErrorSet, .ErrorUnion => return self.fail(scope, operand.src, "error is discarded", .{}), | |
| 2486 | else => return self.constVoid(scope, operand.src), | |
| 2487 | } | |
| 2488 | } | |
| 2489 | ||
| 2490 | fn analyzeInstAlloc(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2491 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstAlloc", .{}); | |
| 2492 | } | |
| 2493 | ||
| 2494 | fn analyzeInstAllocInferred(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 2495 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{}); | |
| 2496 | } | |
| 2497 | ||
| 2498 | fn analyzeInstStore(self: *Module, scope: *Scope, inst: *zir.Inst.Store) InnerError!*Inst { | |
| 2499 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstStore", .{}); | |
| 2500 | } | |
| 2501 | ||
| 2502 | fn analyzeInstParamType(self: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst { | |
| 2503 | const fn_inst = try self.resolveInst(scope, inst.positionals.func); | |
| 2504 | const arg_index = inst.positionals.arg_index; | |
| 2505 | ||
| 2506 | const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) { | |
| 2507 | .Fn => fn_inst.ty, | |
| 2508 | .BoundFn => { | |
| 2509 | return self.fail(scope, fn_inst.src, "TODO implement analyzeInstParamType for method call syntax", .{}); | |
| 2510 | }, | |
| 2511 | else => { | |
| 2512 | return self.fail(scope, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty}); | |
| 2513 | }, | |
| 2514 | }; | |
| 2515 | ||
| 2516 | // TODO support C-style var args | |
| 2517 | const param_count = fn_ty.fnParamLen(); | |
| 2518 | if (arg_index >= param_count) { | |
| 2519 | return self.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} arguments", .{ | |
| 2520 | arg_index, | |
| 2521 | fn_ty, | |
| 2522 | param_count, | |
| 2523 | }); | |
| 2524 | } | |
| 2525 | ||
| 2526 | // TODO support generic functions | |
| 2527 | const param_type = fn_ty.fnParamType(arg_index); | |
| 2528 | return self.constType(scope, inst.base.src, param_type); | |
| 2529 | } | |
| 2530 | ||
| 2531 | fn analyzeInstStr(self: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst { | |
| 2532 | // The bytes references memory inside the ZIR module, which can get deallocated | |
| 2533 | // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. | |
| 2534 | var new_decl_arena = std.heap.ArenaAllocator.init(self.gpa); | |
| 2535 | const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes); | |
| 2536 | ||
| 2537 | const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); | |
| 2538 | ty_payload.* = .{ .len = arena_bytes.len }; | |
| 2539 | ||
| 2540 | const bytes_payload = try scope.arena().create(Value.Payload.Bytes); | |
| 2541 | bytes_payload.* = .{ .data = arena_bytes }; | |
| 2542 | ||
| 2543 | const new_decl = try self.createAnonymousDecl(scope, &new_decl_arena, .{ | |
| 2544 | .ty = Type.initPayload(&ty_payload.base), | |
| 2545 | .val = Value.initPayload(&bytes_payload.base), | |
| 2546 | }); | |
| 2547 | return self.analyzeDeclRef(scope, str_inst.base.src, new_decl); | |
| 2548 | } | |
| 2549 | ||
| 2550 | fn createAnonymousDecl( | |
| 2068 | pub fn createAnonymousDecl( | |
| 2551 | 2069 | self: *Module, |
| 2552 | 2070 | scope: *Scope, |
| 2553 | 2071 | decl_arena: *std.heap.ArenaAllocator, |
| ... | ... | @@ -2593,151 +2111,7 @@ pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*De |
| 2593 | 2111 | return self.decl_table.get(name_hash); |
| 2594 | 2112 | } |
| 2595 | 2113 | |
| 2596 | fn analyzeInstExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst { | |
| 2597 | const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name); | |
| 2598 | const exported_decl = self.lookupDeclName(scope, export_inst.positionals.decl_name) orelse | |
| 2599 | return self.fail(scope, export_inst.base.src, "decl '{}' not found", .{export_inst.positionals.decl_name}); | |
| 2600 | try self.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl); | |
| 2601 | return self.constVoid(scope, export_inst.base.src); | |
| 2602 | } | |
| 2603 | ||
| 2604 | fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst { | |
| 2605 | return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg}); | |
| 2606 | } | |
| 2607 | ||
| 2608 | fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 2609 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 2610 | const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | |
| 2611 | const param_index = b.instructions.items.len; | |
| 2612 | const param_count = fn_ty.fnParamLen(); | |
| 2613 | if (param_index >= param_count) { | |
| 2614 | return self.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{ | |
| 2615 | param_index, | |
| 2616 | param_count, | |
| 2617 | }); | |
| 2618 | } | |
| 2619 | const param_type = fn_ty.fnParamType(param_index); | |
| 2620 | return self.addNoOp(b, inst.base.src, param_type, .arg); | |
| 2621 | } | |
| 2622 | ||
| 2623 | fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst { | |
| 2624 | const parent_block = scope.cast(Scope.Block).?; | |
| 2625 | ||
| 2626 | // Reserve space for a Block instruction so that generated Break instructions can | |
| 2627 | // point to it, even if it doesn't end up getting used because the code ends up being | |
| 2628 | // comptime evaluated. | |
| 2629 | const block_inst = try parent_block.arena.create(Inst.Block); | |
| 2630 | block_inst.* = .{ | |
| 2631 | .base = .{ | |
| 2632 | .tag = Inst.Block.base_tag, | |
| 2633 | .ty = undefined, // Set after analysis. | |
| 2634 | .src = inst.base.src, | |
| 2635 | }, | |
| 2636 | .body = undefined, | |
| 2637 | }; | |
| 2638 | ||
| 2639 | var child_block: Scope.Block = .{ | |
| 2640 | .parent = parent_block, | |
| 2641 | .func = parent_block.func, | |
| 2642 | .decl = parent_block.decl, | |
| 2643 | .instructions = .{}, | |
| 2644 | .arena = parent_block.arena, | |
| 2645 | // TODO @as here is working around a miscompilation compiler bug :( | |
| 2646 | .label = @as(?Scope.Block.Label, Scope.Block.Label{ | |
| 2647 | .zir_block = inst, | |
| 2648 | .results = .{}, | |
| 2649 | .block_inst = block_inst, | |
| 2650 | }), | |
| 2651 | }; | |
| 2652 | const label = &child_block.label.?; | |
| 2653 | ||
| 2654 | defer child_block.instructions.deinit(self.gpa); | |
| 2655 | defer label.results.deinit(self.gpa); | |
| 2656 | ||
| 2657 | try self.analyzeBody(&child_block.base, inst.positionals.body); | |
| 2658 | ||
| 2659 | // Blocks must terminate with noreturn instruction. | |
| 2660 | assert(child_block.instructions.items.len != 0); | |
| 2661 | assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn()); | |
| 2662 | ||
| 2663 | // Need to set the type and emit the Block instruction. This allows machine code generation | |
| 2664 | // to emit a jump instruction to after the block when it encounters the break. | |
| 2665 | try parent_block.instructions.append(self.gpa, &block_inst.base); | |
| 2666 | block_inst.base.ty = try self.resolvePeerTypes(scope, label.results.items); | |
| 2667 | block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; | |
| 2668 | return &block_inst.base; | |
| 2669 | } | |
| 2670 | ||
| 2671 | fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 2672 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 2673 | return self.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint); | |
| 2674 | } | |
| 2675 | ||
| 2676 | fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst { | |
| 2677 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 2678 | const block = inst.positionals.block; | |
| 2679 | return self.analyzeBreak(scope, inst.base.src, block, operand); | |
| 2680 | } | |
| 2681 | ||
| 2682 | fn analyzeInstBreakVoid(self: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst { | |
| 2683 | const block = inst.positionals.block; | |
| 2684 | const void_inst = try self.constVoid(scope, inst.base.src); | |
| 2685 | return self.analyzeBreak(scope, inst.base.src, block, void_inst); | |
| 2686 | } | |
| 2687 | ||
| 2688 | fn analyzeBreak( | |
| 2689 | self: *Module, | |
| 2690 | scope: *Scope, | |
| 2691 | src: usize, | |
| 2692 | zir_block: *zir.Inst.Block, | |
| 2693 | operand: *Inst, | |
| 2694 | ) InnerError!*Inst { | |
| 2695 | var opt_block = scope.cast(Scope.Block); | |
| 2696 | while (opt_block) |block| { | |
| 2697 | if (block.label) |*label| { | |
| 2698 | if (label.zir_block == zir_block) { | |
| 2699 | try label.results.append(self.gpa, operand); | |
| 2700 | const b = try self.requireRuntimeBlock(scope, src); | |
| 2701 | return self.addBr(b, src, label.block_inst, operand); | |
| 2702 | } | |
| 2703 | } | |
| 2704 | opt_block = block.parent; | |
| 2705 | } else unreachable; | |
| 2706 | } | |
| 2707 | ||
| 2708 | fn analyzeInstDeclRefStr(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst { | |
| 2709 | const decl_name = try self.resolveConstString(scope, inst.positionals.name); | |
| 2710 | return self.analyzeDeclRefByName(scope, inst.base.src, decl_name); | |
| 2711 | } | |
| 2712 | ||
| 2713 | fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst { | |
| 2714 | return self.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name); | |
| 2715 | } | |
| 2716 | ||
| 2717 | fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl { | |
| 2718 | const decl_name = inst.positionals.name; | |
| 2719 | const zir_module = scope.namespace().cast(Scope.ZIRModule).?; | |
| 2720 | const src_decl = zir_module.contents.module.findDecl(decl_name) orelse | |
| 2721 | return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name}); | |
| 2722 | ||
| 2723 | const decl = try self.resolveCompleteZirDecl(scope, src_decl.decl); | |
| 2724 | ||
| 2725 | return decl; | |
| 2726 | } | |
| 2727 | ||
| 2728 | fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst { | |
| 2729 | const decl = try self.analyzeDeclVal(scope, inst); | |
| 2730 | const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl); | |
| 2731 | return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); | |
| 2732 | } | |
| 2733 | ||
| 2734 | fn analyzeInstDeclValInModule(self: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst { | |
| 2735 | const decl = inst.positionals.decl; | |
| 2736 | const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl); | |
| 2737 | return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); | |
| 2738 | } | |
| 2739 | ||
| 2740 | fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { | |
| 2114 | pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { | |
| 2741 | 2115 | const scope_decl = scope.decl().?; |
| 2742 | 2116 | try self.declareDeclDependency(scope_decl, decl); |
| 2743 | 2117 | self.ensureDeclAnalyzed(decl) catch |err| { |
| ... | ... | @@ -2765,432 +2139,7 @@ fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerEr |
| 2765 | 2139 | }); |
| 2766 | 2140 | } |
| 2767 | 2141 | |
| 2768 | fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst { | |
| 2769 | const decl = self.lookupDeclName(scope, decl_name) orelse | |
| 2770 | return self.fail(scope, src, "decl '{}' not found", .{decl_name}); | |
| 2771 | return self.analyzeDeclRef(scope, src, decl); | |
| 2772 | } | |
| 2773 | ||
| 2774 | fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { | |
| 2775 | const func = try self.resolveInst(scope, inst.positionals.func); | |
| 2776 | if (func.ty.zigTypeTag() != .Fn) | |
| 2777 | return self.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty}); | |
| 2778 | ||
| 2779 | const cc = func.ty.fnCallingConvention(); | |
| 2780 | if (cc == .Naked) { | |
| 2781 | // TODO add error note: declared here | |
| 2782 | return self.fail( | |
| 2783 | scope, | |
| 2784 | inst.positionals.func.src, | |
| 2785 | "unable to call function with naked calling convention", | |
| 2786 | .{}, | |
| 2787 | ); | |
| 2788 | } | |
| 2789 | const call_params_len = inst.positionals.args.len; | |
| 2790 | const fn_params_len = func.ty.fnParamLen(); | |
| 2791 | if (func.ty.fnIsVarArgs()) { | |
| 2792 | if (call_params_len < fn_params_len) { | |
| 2793 | // TODO add error note: declared here | |
| 2794 | return self.fail( | |
| 2795 | scope, | |
| 2796 | inst.positionals.func.src, | |
| 2797 | "expected at least {} arguments, found {}", | |
| 2798 | .{ fn_params_len, call_params_len }, | |
| 2799 | ); | |
| 2800 | } | |
| 2801 | return self.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{}); | |
| 2802 | } else if (fn_params_len != call_params_len) { | |
| 2803 | // TODO add error note: declared here | |
| 2804 | return self.fail( | |
| 2805 | scope, | |
| 2806 | inst.positionals.func.src, | |
| 2807 | "expected {} arguments, found {}", | |
| 2808 | .{ fn_params_len, call_params_len }, | |
| 2809 | ); | |
| 2810 | } | |
| 2811 | ||
| 2812 | if (inst.kw_args.modifier == .compile_time) { | |
| 2813 | return self.fail(scope, inst.base.src, "TODO implement comptime function calls", .{}); | |
| 2814 | } | |
| 2815 | if (inst.kw_args.modifier != .auto) { | |
| 2816 | return self.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier}); | |
| 2817 | } | |
| 2818 | ||
| 2819 | // TODO handle function calls of generic functions | |
| 2820 | ||
| 2821 | const fn_param_types = try self.gpa.alloc(Type, fn_params_len); | |
| 2822 | defer self.gpa.free(fn_param_types); | |
| 2823 | func.ty.fnParamTypes(fn_param_types); | |
| 2824 | ||
| 2825 | const casted_args = try scope.arena().alloc(*Inst, fn_params_len); | |
| 2826 | for (inst.positionals.args) |src_arg, i| { | |
| 2827 | const uncasted_arg = try self.resolveInst(scope, src_arg); | |
| 2828 | casted_args[i] = try self.coerce(scope, fn_param_types[i], uncasted_arg); | |
| 2829 | } | |
| 2830 | ||
| 2831 | const ret_type = func.ty.fnReturnType(); | |
| 2832 | ||
| 2833 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 2834 | return self.addCall(b, inst.base.src, ret_type, func, casted_args); | |
| 2835 | } | |
| 2836 | ||
| 2837 | fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { | |
| 2838 | const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type); | |
| 2839 | const fn_zir = blk: { | |
| 2840 | var fn_arena = std.heap.ArenaAllocator.init(self.gpa); | |
| 2841 | errdefer fn_arena.deinit(); | |
| 2842 | ||
| 2843 | const fn_zir = try scope.arena().create(Fn.ZIR); | |
| 2844 | fn_zir.* = .{ | |
| 2845 | .body = .{ | |
| 2846 | .instructions = fn_inst.positionals.body.instructions, | |
| 2847 | }, | |
| 2848 | .arena = fn_arena.state, | |
| 2849 | }; | |
| 2850 | break :blk fn_zir; | |
| 2851 | }; | |
| 2852 | const new_func = try scope.arena().create(Fn); | |
| 2853 | new_func.* = .{ | |
| 2854 | .analysis = .{ .queued = fn_zir }, | |
| 2855 | .owner_decl = scope.decl().?, | |
| 2856 | }; | |
| 2857 | const fn_payload = try scope.arena().create(Value.Payload.Function); | |
| 2858 | fn_payload.* = .{ .func = new_func }; | |
| 2859 | return self.constInst(scope, fn_inst.base.src, .{ | |
| 2860 | .ty = fn_type, | |
| 2861 | .val = Value.initPayload(&fn_payload.base), | |
| 2862 | }); | |
| 2863 | } | |
| 2864 | ||
| 2865 | fn analyzeInstIntType(self: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst { | |
| 2866 | return self.fail(scope, inttype.base.src, "TODO implement inttype", .{}); | |
| 2867 | } | |
| 2868 | ||
| 2869 | fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { | |
| 2870 | const return_type = try self.resolveType(scope, fntype.positionals.return_type); | |
| 2871 | ||
| 2872 | // Hot path for some common function types. | |
| 2873 | if (fntype.positionals.param_types.len == 0) { | |
| 2874 | if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Unspecified) { | |
| 2875 | return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args)); | |
| 2876 | } | |
| 2877 | ||
| 2878 | if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .Unspecified) { | |
| 2879 | return self.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args)); | |
| 2880 | } | |
| 2881 | ||
| 2882 | if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Naked) { | |
| 2883 | return self.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args)); | |
| 2884 | } | |
| 2885 | ||
| 2886 | if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .C) { | |
| 2887 | return self.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args)); | |
| 2888 | } | |
| 2889 | } | |
| 2890 | ||
| 2891 | const arena = scope.arena(); | |
| 2892 | const param_types = try arena.alloc(Type, fntype.positionals.param_types.len); | |
| 2893 | for (fntype.positionals.param_types) |param_type, i| { | |
| 2894 | param_types[i] = try self.resolveType(scope, param_type); | |
| 2895 | } | |
| 2896 | ||
| 2897 | const payload = try arena.create(Type.Payload.Function); | |
| 2898 | payload.* = .{ | |
| 2899 | .cc = fntype.kw_args.cc, | |
| 2900 | .return_type = return_type, | |
| 2901 | .param_types = param_types, | |
| 2902 | }; | |
| 2903 | return self.constType(scope, fntype.base.src, Type.initPayload(&payload.base)); | |
| 2904 | } | |
| 2905 | ||
| 2906 | fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { | |
| 2907 | return self.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue()); | |
| 2908 | } | |
| 2909 | ||
| 2910 | fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst { | |
| 2911 | const dest_type = try self.resolveType(scope, as.positionals.lhs); | |
| 2912 | const new_inst = try self.resolveInst(scope, as.positionals.rhs); | |
| 2913 | return self.coerce(scope, dest_type, new_inst); | |
| 2914 | } | |
| 2915 | ||
| 2916 | fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2917 | const ptr = try self.resolveInst(scope, ptrtoint.positionals.operand); | |
| 2918 | if (ptr.ty.zigTypeTag() != .Pointer) { | |
| 2919 | return self.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty}); | |
| 2920 | } | |
| 2921 | // TODO handle known-pointer-address | |
| 2922 | const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src); | |
| 2923 | const ty = Type.initTag(.usize); | |
| 2924 | return self.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr); | |
| 2925 | } | |
| 2926 | ||
| 2927 | fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst { | |
| 2928 | const object_ptr = try self.resolveInst(scope, fieldptr.positionals.object_ptr); | |
| 2929 | const field_name = try self.resolveConstString(scope, fieldptr.positionals.field_name); | |
| 2930 | ||
| 2931 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { | |
| 2932 | .Pointer => object_ptr.ty.elemType(), | |
| 2933 | else => return self.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}), | |
| 2934 | }; | |
| 2935 | switch (elem_ty.zigTypeTag()) { | |
| 2936 | .Array => { | |
| 2937 | if (mem.eql(u8, field_name, "len")) { | |
| 2938 | const len_payload = try scope.arena().create(Value.Payload.Int_u64); | |
| 2939 | len_payload.* = .{ .int = elem_ty.arrayLen() }; | |
| 2940 | ||
| 2941 | const ref_payload = try scope.arena().create(Value.Payload.RefVal); | |
| 2942 | ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) }; | |
| 2943 | ||
| 2944 | return self.constInst(scope, fieldptr.base.src, .{ | |
| 2945 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), | |
| 2946 | .val = Value.initPayload(&ref_payload.base), | |
| 2947 | }); | |
| 2948 | } else { | |
| 2949 | return self.fail( | |
| 2950 | scope, | |
| 2951 | fieldptr.positionals.field_name.src, | |
| 2952 | "no member named '{}' in '{}'", | |
| 2953 | .{ field_name, elem_ty }, | |
| 2954 | ); | |
| 2955 | } | |
| 2956 | }, | |
| 2957 | else => return self.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}), | |
| 2958 | } | |
| 2959 | } | |
| 2960 | ||
| 2961 | fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 2962 | const dest_type = try self.resolveType(scope, inst.positionals.lhs); | |
| 2963 | const operand = try self.resolveInst(scope, inst.positionals.rhs); | |
| 2964 | ||
| 2965 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { | |
| 2966 | .ComptimeInt => true, | |
| 2967 | .Int => false, | |
| 2968 | else => return self.fail( | |
| 2969 | scope, | |
| 2970 | inst.positionals.lhs.src, | |
| 2971 | "expected integer type, found '{}'", | |
| 2972 | .{ | |
| 2973 | dest_type, | |
| 2974 | }, | |
| 2975 | ), | |
| 2976 | }; | |
| 2977 | ||
| 2978 | switch (operand.ty.zigTypeTag()) { | |
| 2979 | .ComptimeInt, .Int => {}, | |
| 2980 | else => return self.fail( | |
| 2981 | scope, | |
| 2982 | inst.positionals.rhs.src, | |
| 2983 | "expected integer type, found '{}'", | |
| 2984 | .{operand.ty}, | |
| 2985 | ), | |
| 2986 | } | |
| 2987 | ||
| 2988 | if (operand.value() != null) { | |
| 2989 | return self.coerce(scope, dest_type, operand); | |
| 2990 | } else if (dest_is_comptime_int) { | |
| 2991 | return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{}); | |
| 2992 | } | |
| 2993 | ||
| 2994 | return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{}); | |
| 2995 | } | |
| 2996 | ||
| 2997 | fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 2998 | const dest_type = try self.resolveType(scope, inst.positionals.lhs); | |
| 2999 | const operand = try self.resolveInst(scope, inst.positionals.rhs); | |
| 3000 | return self.bitcast(scope, dest_type, operand); | |
| 3001 | } | |
| 3002 | ||
| 3003 | fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 3004 | const dest_type = try self.resolveType(scope, inst.positionals.lhs); | |
| 3005 | const operand = try self.resolveInst(scope, inst.positionals.rhs); | |
| 3006 | ||
| 3007 | const dest_is_comptime_float = switch (dest_type.zigTypeTag()) { | |
| 3008 | .ComptimeFloat => true, | |
| 3009 | .Float => false, | |
| 3010 | else => return self.fail( | |
| 3011 | scope, | |
| 3012 | inst.positionals.lhs.src, | |
| 3013 | "expected float type, found '{}'", | |
| 3014 | .{ | |
| 3015 | dest_type, | |
| 3016 | }, | |
| 3017 | ), | |
| 3018 | }; | |
| 3019 | ||
| 3020 | switch (operand.ty.zigTypeTag()) { | |
| 3021 | .ComptimeFloat, .Float, .ComptimeInt => {}, | |
| 3022 | else => return self.fail( | |
| 3023 | scope, | |
| 3024 | inst.positionals.rhs.src, | |
| 3025 | "expected float type, found '{}'", | |
| 3026 | .{operand.ty}, | |
| 3027 | ), | |
| 3028 | } | |
| 3029 | ||
| 3030 | if (operand.value() != null) { | |
| 3031 | return self.coerce(scope, dest_type, operand); | |
| 3032 | } else if (dest_is_comptime_float) { | |
| 3033 | return self.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{}); | |
| 3034 | } | |
| 3035 | ||
| 3036 | return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{}); | |
| 3037 | } | |
| 3038 | ||
| 3039 | fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst { | |
| 3040 | const array_ptr = try self.resolveInst(scope, inst.positionals.array_ptr); | |
| 3041 | const uncasted_index = try self.resolveInst(scope, inst.positionals.index); | |
| 3042 | const elem_index = try self.coerce(scope, Type.initTag(.usize), uncasted_index); | |
| 3043 | ||
| 3044 | if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) { | |
| 3045 | if (array_ptr.value()) |array_ptr_val| { | |
| 3046 | if (elem_index.value()) |index_val| { | |
| 3047 | // Both array pointer and index are compile-time known. | |
| 3048 | const index_u64 = index_val.toUnsignedInt(); | |
| 3049 | // @intCast here because it would have been impossible to construct a value that | |
| 3050 | // required a larger index. | |
| 3051 | const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64)); | |
| 3052 | ||
| 3053 | const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer); | |
| 3054 | type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() }; | |
| 3055 | ||
| 3056 | return self.constInst(scope, inst.base.src, .{ | |
| 3057 | .ty = Type.initPayload(&type_payload.base), | |
| 3058 | .val = elem_ptr, | |
| 3059 | }); | |
| 3060 | } | |
| 3061 | } | |
| 3062 | } | |
| 3063 | ||
| 3064 | return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{}); | |
| 3065 | } | |
| 3066 | ||
| 3067 | fn floatOpAllowed(tag: zir.Inst.Tag) bool { | |
| 3068 | // extend this swich as additional operators are implemented | |
| 3069 | return switch (tag) { | |
| 3070 | .add, .sub => true, | |
| 3071 | else => false, | |
| 3072 | }; | |
| 3073 | } | |
| 3074 | ||
| 3075 | fn analyzeInstShl(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 3076 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{}); | |
| 3077 | } | |
| 3078 | ||
| 3079 | fn analyzeInstShr(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 3080 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstShr", .{}); | |
| 3081 | } | |
| 3082 | ||
| 3083 | fn analyzeInstBitwise(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 3084 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{}); | |
| 3085 | } | |
| 3086 | ||
| 3087 | fn analyzeInstArrayCat(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 3088 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{}); | |
| 3089 | } | |
| 3090 | ||
| 3091 | fn analyzeInstArrayMul(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 3092 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstArrayMul", .{}); | |
| 3093 | } | |
| 3094 | ||
| 3095 | fn analyzeInstArithmetic(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 3096 | const tracy = trace(@src()); | |
| 3097 | defer tracy.end(); | |
| 3098 | ||
| 3099 | const lhs = try self.resolveInst(scope, inst.positionals.lhs); | |
| 3100 | const rhs = try self.resolveInst(scope, inst.positionals.rhs); | |
| 3101 | ||
| 3102 | const instructions = &[_]*Inst{ lhs, rhs }; | |
| 3103 | const resolved_type = try self.resolvePeerTypes(scope, instructions); | |
| 3104 | const casted_lhs = try self.coerce(scope, resolved_type, lhs); | |
| 3105 | const casted_rhs = try self.coerce(scope, resolved_type, rhs); | |
| 3106 | ||
| 3107 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) | |
| 3108 | resolved_type.elemType() | |
| 3109 | else | |
| 3110 | resolved_type; | |
| 3111 | ||
| 3112 | const scalar_tag = scalar_type.zigTypeTag(); | |
| 3113 | ||
| 3114 | if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { | |
| 3115 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | |
| 3116 | return self.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{ | |
| 3117 | lhs.ty.arrayLen(), | |
| 3118 | rhs.ty.arrayLen(), | |
| 3119 | }); | |
| 3120 | } | |
| 3121 | return self.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{}); | |
| 3122 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { | |
| 3123 | return self.fail(scope, inst.base.src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | |
| 3124 | lhs.ty, | |
| 3125 | rhs.ty, | |
| 3126 | }); | |
| 3127 | } | |
| 3128 | ||
| 3129 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; | |
| 3130 | const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; | |
| 3131 | ||
| 3132 | if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) { | |
| 3133 | return self.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); | |
| 3134 | } | |
| 3135 | ||
| 3136 | if (casted_lhs.value()) |lhs_val| { | |
| 3137 | if (casted_rhs.value()) |rhs_val| { | |
| 3138 | return self.analyzeInstComptimeOp(scope, scalar_type, inst, lhs_val, rhs_val); | |
| 3139 | } | |
| 3140 | } | |
| 3141 | ||
| 3142 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 3143 | const ir_tag = switch (inst.base.tag) { | |
| 3144 | .add => Inst.Tag.add, | |
| 3145 | .sub => Inst.Tag.sub, | |
| 3146 | else => return self.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}), | |
| 3147 | }; | |
| 3148 | ||
| 3149 | return self.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); | |
| 3150 | } | |
| 3151 | ||
| 3152 | /// Analyzes operands that are known at comptime | |
| 3153 | fn analyzeInstComptimeOp(self: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst { | |
| 3154 | // incase rhs is 0, simply return lhs without doing any calculations | |
| 3155 | // TODO Once division is implemented we should throw an error when dividing by 0. | |
| 3156 | if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) { | |
| 3157 | return self.constInst(scope, inst.base.src, .{ | |
| 3158 | .ty = res_type, | |
| 3159 | .val = lhs_val, | |
| 3160 | }); | |
| 3161 | } | |
| 3162 | const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt; | |
| 3163 | ||
| 3164 | const value = try switch (inst.base.tag) { | |
| 3165 | .add => blk: { | |
| 3166 | const val = if (is_int) | |
| 3167 | intAdd(scope.arena(), lhs_val, rhs_val) | |
| 3168 | else | |
| 3169 | self.floatAdd(scope, res_type, inst, lhs_val, rhs_val); | |
| 3170 | break :blk val; | |
| 3171 | }, | |
| 3172 | .sub => blk: { | |
| 3173 | const val = if (is_int) | |
| 3174 | intSub(scope.arena(), lhs_val, rhs_val) | |
| 3175 | else | |
| 3176 | self.floatSub(scope, res_type, inst, lhs_val, rhs_val); | |
| 3177 | break :blk val; | |
| 3178 | }, | |
| 3179 | else => return self.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}), | |
| 3180 | }; | |
| 3181 | ||
| 3182 | return self.constInst(scope, inst.base.src, .{ | |
| 3183 | .ty = res_type, | |
| 3184 | .val = value, | |
| 3185 | }); | |
| 3186 | } | |
| 3187 | ||
| 3188 | fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { | |
| 3189 | const ptr = try self.resolveInst(scope, deref.positionals.operand); | |
| 3190 | return self.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.operand.src); | |
| 3191 | } | |
| 3192 | ||
| 3193 | fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst { | |
| 2142 | pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst { | |
| 3194 | 2143 | const elem_ty = switch (ptr.ty.zigTypeTag()) { |
| 3195 | 2144 | .Pointer => ptr.ty.elemType(), |
| 3196 | 2145 | else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}), |
| ... | ... | @@ -3205,165 +2154,13 @@ fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: u |
| 3205 | 2154 | return self.fail(scope, src, "TODO implement runtime deref", .{}); |
| 3206 | 2155 | } |
| 3207 | 2156 | |
| 3208 | fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst { | |
| 3209 | const return_type = try self.resolveType(scope, assembly.positionals.return_type); | |
| 3210 | const asm_source = try self.resolveConstString(scope, assembly.positionals.asm_source); | |
| 3211 | const output = if (assembly.kw_args.output) |o| try self.resolveConstString(scope, o) else null; | |
| 3212 | ||
| 3213 | const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len); | |
| 3214 | const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len); | |
| 3215 | const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len); | |
| 3216 | ||
| 3217 | for (inputs) |*elem, i| { | |
| 3218 | elem.* = try self.resolveConstString(scope, assembly.kw_args.inputs[i]); | |
| 3219 | } | |
| 3220 | for (clobbers) |*elem, i| { | |
| 3221 | elem.* = try self.resolveConstString(scope, assembly.kw_args.clobbers[i]); | |
| 3222 | } | |
| 3223 | for (args) |*elem, i| { | |
| 3224 | const arg = try self.resolveInst(scope, assembly.kw_args.args[i]); | |
| 3225 | elem.* = try self.coerce(scope, Type.initTag(.usize), arg); | |
| 3226 | } | |
| 3227 | ||
| 3228 | const b = try self.requireRuntimeBlock(scope, assembly.base.src); | |
| 3229 | const inst = try b.arena.create(Inst.Assembly); | |
| 3230 | inst.* = .{ | |
| 3231 | .base = .{ | |
| 3232 | .tag = .assembly, | |
| 3233 | .ty = return_type, | |
| 3234 | .src = assembly.base.src, | |
| 3235 | }, | |
| 3236 | .asm_source = asm_source, | |
| 3237 | .is_volatile = assembly.kw_args.@"volatile", | |
| 3238 | .output = output, | |
| 3239 | .inputs = inputs, | |
| 3240 | .clobbers = clobbers, | |
| 3241 | .args = args, | |
| 3242 | }; | |
| 3243 | try b.instructions.append(self.gpa, &inst.base); | |
| 3244 | return &inst.base; | |
| 3245 | } | |
| 3246 | ||
| 3247 | fn analyzeInstCmp( | |
| 3248 | self: *Module, | |
| 3249 | scope: *Scope, | |
| 3250 | inst: *zir.Inst.BinOp, | |
| 3251 | op: std.math.CompareOperator, | |
| 3252 | ) InnerError!*Inst { | |
| 3253 | const lhs = try self.resolveInst(scope, inst.positionals.lhs); | |
| 3254 | const rhs = try self.resolveInst(scope, inst.positionals.rhs); | |
| 3255 | ||
| 3256 | const is_equality_cmp = switch (op) { | |
| 3257 | .eq, .neq => true, | |
| 3258 | else => false, | |
| 3259 | }; | |
| 3260 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | |
| 3261 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | |
| 3262 | if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) { | |
| 3263 | // null == null, null != null | |
| 3264 | return self.constBool(scope, inst.base.src, op == .eq); | |
| 3265 | } else if (is_equality_cmp and | |
| 3266 | ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or | |
| 3267 | rhs_ty_tag == .Null and lhs_ty_tag == .Optional)) | |
| 3268 | { | |
| 3269 | // comparing null with optionals | |
| 3270 | const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; | |
| 3271 | if (opt_operand.value()) |opt_val| { | |
| 3272 | const is_null = opt_val.isNull(); | |
| 3273 | return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null); | |
| 3274 | } | |
| 3275 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 3276 | const inst_tag: Inst.Tag = switch (op) { | |
| 3277 | .eq => .isnull, | |
| 3278 | .neq => .isnonnull, | |
| 3279 | else => unreachable, | |
| 3280 | }; | |
| 3281 | return self.addUnOp(b, inst.base.src, Type.initTag(.bool), inst_tag, opt_operand); | |
| 3282 | } else if (is_equality_cmp and | |
| 3283 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) | |
| 3284 | { | |
| 3285 | return self.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{}); | |
| 3286 | } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { | |
| 3287 | const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty; | |
| 3288 | return self.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type}); | |
| 3289 | } else if (is_equality_cmp and | |
| 3290 | ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or | |
| 3291 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) | |
| 3292 | { | |
| 3293 | return self.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | |
| 3294 | } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { | |
| 3295 | if (!is_equality_cmp) { | |
| 3296 | return self.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)}); | |
| 3297 | } | |
| 3298 | return self.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{}); | |
| 3299 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { | |
| 3300 | // This operation allows any combination of integer and float types, regardless of the | |
| 3301 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for | |
| 3302 | // numeric types. | |
| 3303 | return self.cmpNumeric(scope, inst.base.src, lhs, rhs, op); | |
| 3304 | } | |
| 3305 | return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{}); | |
| 3306 | } | |
| 3307 | ||
| 3308 | fn analyzeInstTypeOf(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 3309 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 3310 | return self.constType(scope, inst.base.src, operand.ty); | |
| 3311 | } | |
| 3312 | ||
| 3313 | fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 3314 | const uncasted_operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 3315 | const bool_type = Type.initTag(.bool); | |
| 3316 | const operand = try self.coerce(scope, bool_type, uncasted_operand); | |
| 3317 | if (try self.resolveDefinedValue(scope, operand)) |val| { | |
| 3318 | return self.constBool(scope, inst.base.src, !val.toBool()); | |
| 3319 | } | |
| 3320 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 3321 | return self.addUnOp(b, inst.base.src, bool_type, .not, operand); | |
| 3322 | } | |
| 3323 | ||
| 3324 | fn analyzeInstIsNonNull(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { | |
| 3325 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 3326 | return self.analyzeIsNull(scope, inst.base.src, operand, invert_logic); | |
| 3327 | } | |
| 3328 | ||
| 3329 | fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst { | |
| 3330 | const uncasted_cond = try self.resolveInst(scope, inst.positionals.condition); | |
| 3331 | const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond); | |
| 3332 | ||
| 3333 | if (try self.resolveDefinedValue(scope, cond)) |cond_val| { | |
| 3334 | const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body; | |
| 3335 | try self.analyzeBody(scope, body.*); | |
| 3336 | return self.constVoid(scope, inst.base.src); | |
| 3337 | } | |
| 3338 | ||
| 3339 | const parent_block = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 3340 | ||
| 3341 | var true_block: Scope.Block = .{ | |
| 3342 | .parent = parent_block, | |
| 3343 | .func = parent_block.func, | |
| 3344 | .decl = parent_block.decl, | |
| 3345 | .instructions = .{}, | |
| 3346 | .arena = parent_block.arena, | |
| 3347 | }; | |
| 3348 | defer true_block.instructions.deinit(self.gpa); | |
| 3349 | try self.analyzeBody(&true_block.base, inst.positionals.then_body); | |
| 3350 | ||
| 3351 | var false_block: Scope.Block = .{ | |
| 3352 | .parent = parent_block, | |
| 3353 | .func = parent_block.func, | |
| 3354 | .decl = parent_block.decl, | |
| 3355 | .instructions = .{}, | |
| 3356 | .arena = parent_block.arena, | |
| 3357 | }; | |
| 3358 | defer false_block.instructions.deinit(self.gpa); | |
| 3359 | try self.analyzeBody(&false_block.base, inst.positionals.else_body); | |
| 3360 | ||
| 3361 | const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) }; | |
| 3362 | const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) }; | |
| 3363 | return self.addCondBr(parent_block, inst.base.src, cond, then_body, else_body); | |
| 2157 | pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst { | |
| 2158 | const decl = self.lookupDeclName(scope, decl_name) orelse | |
| 2159 | return self.fail(scope, src, "decl '{}' not found", .{decl_name}); | |
| 2160 | return self.analyzeDeclRef(scope, src, decl); | |
| 3364 | 2161 | } |
| 3365 | 2162 | |
| 3366 | fn wantSafety(self: *Module, scope: *Scope) bool { | |
| 2163 | pub fn wantSafety(self: *Module, scope: *Scope) bool { | |
| 3367 | 2164 | return switch (self.optimize_mode) { |
| 3368 | 2165 | .Debug => true, |
| 3369 | 2166 | .ReleaseSafe => true, |
| ... | ... | @@ -3372,42 +2169,12 @@ fn wantSafety(self: *Module, scope: *Scope) bool { |
| 3372 | 2169 | }; |
| 3373 | 2170 | } |
| 3374 | 2171 | |
| 3375 | fn analyzeUnreach(self: *Module, scope: *Scope, src: usize) InnerError!*Inst { | |
| 2172 | pub fn analyzeUnreach(self: *Module, scope: *Scope, src: usize) InnerError!*Inst { | |
| 3376 | 2173 | const b = try self.requireRuntimeBlock(scope, src); |
| 3377 | 2174 | return self.addNoOp(b, src, Type.initTag(.noreturn), .unreach); |
| 3378 | 2175 | } |
| 3379 | 2176 | |
| 3380 | fn analyzeInstUnreachNoChk(self: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { | |
| 3381 | return self.analyzeUnreach(scope, unreach.base.src); | |
| 3382 | } | |
| 3383 | ||
| 3384 | fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { | |
| 3385 | const b = try self.requireRuntimeBlock(scope, unreach.base.src); | |
| 3386 | if (self.wantSafety(scope)) { | |
| 3387 | // TODO Once we have a panic function to call, call it here instead of this. | |
| 3388 | _ = try self.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint); | |
| 3389 | } | |
| 3390 | return self.analyzeUnreach(scope, unreach.base.src); | |
| 3391 | } | |
| 3392 | ||
| 3393 | fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 3394 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 3395 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 3396 | return self.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand); | |
| 3397 | } | |
| 3398 | ||
| 3399 | fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 3400 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 3401 | return self.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid); | |
| 3402 | } | |
| 3403 | ||
| 3404 | fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void { | |
| 3405 | for (body.instructions) |src_inst| { | |
| 3406 | src_inst.analyzed_inst = try self.analyzeInst(scope, src_inst); | |
| 3407 | } | |
| 3408 | } | |
| 3409 | ||
| 3410 | fn analyzeIsNull( | |
| 2177 | pub fn analyzeIsNull( | |
| 3411 | 2178 | self: *Module, |
| 3412 | 2179 | scope: *Scope, |
| 3413 | 2180 | src: usize, |
| ... | ... | @@ -3418,7 +2185,7 @@ fn analyzeIsNull( |
| 3418 | 2185 | } |
| 3419 | 2186 | |
| 3420 | 2187 | /// Asserts that lhs and rhs types are both numeric. |
| 3421 | fn cmpNumeric( | |
| 2188 | pub fn cmpNumeric( | |
| 3422 | 2189 | self: *Module, |
| 3423 | 2190 | scope: *Scope, |
| 3424 | 2191 | src: usize, |
| ... | ... | @@ -3601,7 +2368,7 @@ fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type { |
| 3601 | 2368 | } |
| 3602 | 2369 | } |
| 3603 | 2370 | |
| 3604 | fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type { | |
| 2371 | pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type { | |
| 3605 | 2372 | if (instructions.len == 0) |
| 3606 | 2373 | return Type.initTag(.noreturn); |
| 3607 | 2374 | |
| ... | ... | @@ -3641,7 +2408,7 @@ fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type { |
| 3641 | 2408 | return prev_inst.ty; |
| 3642 | 2409 | } |
| 3643 | 2410 | |
| 3644 | fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | |
| 2411 | pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | |
| 3645 | 2412 | // If the types are the same, we can return the operand. |
| 3646 | 2413 | if (dest_type.eql(inst.ty)) |
| 3647 | 2414 | return inst; |
| ... | ... | @@ -3737,7 +2504,7 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { |
| 3737 | 2504 | return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type }); |
| 3738 | 2505 | } |
| 3739 | 2506 | |
| 3740 | fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | |
| 2507 | pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | |
| 3741 | 2508 | if (inst.value()) |val| { |
| 3742 | 2509 | // Keep the comptime Value representation; take the new type. |
| 3743 | 2510 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); |
| ... | ... | @@ -3884,7 +2651,7 @@ fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool { |
| 3884 | 2651 | return @bitCast(u128, a) == @bitCast(u128, b); |
| 3885 | 2652 | } |
| 3886 | 2653 | |
| 3887 | fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value { | |
| 2654 | pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value { | |
| 3888 | 2655 | // TODO is this a performance issue? maybe we should try the operation without |
| 3889 | 2656 | // resorting to BigInt first. |
| 3890 | 2657 | var lhs_space: Value.BigIntSpace = undefined; |
| ... | ... | @@ -3912,7 +2679,7 @@ fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value { |
| 3912 | 2679 | return Value.initPayload(val_payload); |
| 3913 | 2680 | } |
| 3914 | 2681 | |
| 3915 | fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { | |
| 2682 | pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { | |
| 3916 | 2683 | // TODO is this a performance issue? maybe we should try the operation without |
| 3917 | 2684 | // resorting to BigInt first. |
| 3918 | 2685 | var lhs_space: Value.BigIntSpace = undefined; |
| ... | ... | @@ -3940,7 +2707,7 @@ fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { |
| 3940 | 2707 | return Value.initPayload(val_payload); |
| 3941 | 2708 | } |
| 3942 | 2709 | |
| 3943 | fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinOp, lhs: Value, rhs: Value) !Value { | |
| 2710 | pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value { | |
| 3944 | 2711 | var bit_count = switch (float_type.tag()) { |
| 3945 | 2712 | .comptime_float => 128, |
| 3946 | 2713 | else => float_type.floatBits(self.target()), |
| ... | ... | @@ -3949,7 +2716,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO |
| 3949 | 2716 | const allocator = scope.arena(); |
| 3950 | 2717 | const val_payload = switch (bit_count) { |
| 3951 | 2718 | 16 => { |
| 3952 | return self.fail(scope, inst.base.src, "TODO Implement addition for soft floats", .{}); | |
| 2719 | return self.fail(scope, src, "TODO Implement addition for soft floats", .{}); | |
| 3953 | 2720 | }, |
| 3954 | 2721 | 32 => blk: { |
| 3955 | 2722 | const lhs_val = lhs.toFloat(f32); |
| ... | ... | @@ -3966,7 +2733,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO |
| 3966 | 2733 | break :blk &val_payload.base; |
| 3967 | 2734 | }, |
| 3968 | 2735 | 128 => blk: { |
| 3969 | return self.fail(scope, inst.base.src, "TODO Implement addition for big floats", .{}); | |
| 2736 | return self.fail(scope, src, "TODO Implement addition for big floats", .{}); | |
| 3970 | 2737 | }, |
| 3971 | 2738 | else => unreachable, |
| 3972 | 2739 | }; |
| ... | ... | @@ -3974,7 +2741,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO |
| 3974 | 2741 | return Value.initPayload(val_payload); |
| 3975 | 2742 | } |
| 3976 | 2743 | |
| 3977 | fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinOp, lhs: Value, rhs: Value) !Value { | |
| 2744 | pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value { | |
| 3978 | 2745 | var bit_count = switch (float_type.tag()) { |
| 3979 | 2746 | .comptime_float => 128, |
| 3980 | 2747 | else => float_type.floatBits(self.target()), |
| ... | ... | @@ -3983,7 +2750,7 @@ fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO |
| 3983 | 2750 | const allocator = scope.arena(); |
| 3984 | 2751 | const val_payload = switch (bit_count) { |
| 3985 | 2752 | 16 => { |
| 3986 | return self.fail(scope, inst.base.src, "TODO Implement substraction for soft floats", .{}); | |
| 2753 | return self.fail(scope, src, "TODO Implement substraction for soft floats", .{}); | |
| 3987 | 2754 | }, |
| 3988 | 2755 | 32 => blk: { |
| 3989 | 2756 | const lhs_val = lhs.toFloat(f32); |
| ... | ... | @@ -4000,7 +2767,7 @@ fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO |
| 4000 | 2767 | break :blk &val_payload.base; |
| 4001 | 2768 | }, |
| 4002 | 2769 | 128 => blk: { |
| 4003 | return self.fail(scope, inst.base.src, "TODO Implement substraction for big floats", .{}); | |
| 2770 | return self.fail(scope, src, "TODO Implement substraction for big floats", .{}); | |
| 4004 | 2771 | }, |
| 4005 | 2772 | else => unreachable, |
| 4006 | 2773 | }; |
src-self-hosted/astgen.zig+184-66| ... | ... | @@ -36,7 +36,7 @@ pub const ResultLoc = union(enum) { |
| 36 | 36 | |
| 37 | 37 | pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst { |
| 38 | 38 | const type_src = scope.tree().token_locs[type_node.firstToken()].start; |
| 39 | const type_type = try mod.addZIRInstConst(scope, type_src, .{ | |
| 39 | const type_type = try addZIRInstConst(mod, scope, type_src, .{ | |
| 40 | 40 | .ty = Type.initTag(.type), |
| 41 | 41 | .val = Value.initTag(.type_type), |
| 42 | 42 | }); |
| ... | ... | @@ -146,7 +146,7 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block |
| 146 | 146 | else => { |
| 147 | 147 | const possibly_unused_result = try expr(mod, scope, .none, statement); |
| 148 | 148 | const src = scope.tree().token_locs[statement.firstToken()].start; |
| 149 | _ = try mod.addZIRUnOp(scope, src, .ensure_result_used, possibly_unused_result); | |
| 149 | _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result); | |
| 150 | 150 | }, |
| 151 | 151 | } |
| 152 | 152 | } |
| ... | ... | @@ -177,7 +177,7 @@ fn varDecl( |
| 177 | 177 | if (nodeMayNeedMemoryLocation(init_node)) { |
| 178 | 178 | if (node.getTrailer("type_node")) |type_node| { |
| 179 | 179 | const type_inst = try typeExpr(mod, scope, type_node); |
| 180 | const alloc = try mod.addZIRUnOp(scope, name_src, .alloc, type_inst); | |
| 180 | const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst); | |
| 181 | 181 | const result_loc: ResultLoc = .{ .ptr = alloc }; |
| 182 | 182 | const init_inst = try expr(mod, scope, result_loc, init_node); |
| 183 | 183 | const sub_scope = try block_arena.create(Scope.LocalVal); |
| ... | ... | @@ -189,7 +189,7 @@ fn varDecl( |
| 189 | 189 | }; |
| 190 | 190 | return &sub_scope.base; |
| 191 | 191 | } else { |
| 192 | const alloc = try mod.addZIRNoOpT(scope, name_src, .alloc_inferred); | |
| 192 | const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred); | |
| 193 | 193 | const result_loc: ResultLoc = .{ .inferred_ptr = alloc }; |
| 194 | 194 | const init_inst = try expr(mod, scope, result_loc, init_node); |
| 195 | 195 | const sub_scope = try block_arena.create(Scope.LocalVal); |
| ... | ... | @@ -220,7 +220,7 @@ fn varDecl( |
| 220 | 220 | .Keyword_var => { |
| 221 | 221 | if (node.getTrailer("type_node")) |type_node| { |
| 222 | 222 | const type_inst = try typeExpr(mod, scope, type_node); |
| 223 | const alloc = try mod.addZIRUnOp(scope, name_src, .alloc, type_inst); | |
| 223 | const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst); | |
| 224 | 224 | const result_loc: ResultLoc = .{ .ptr = alloc }; |
| 225 | 225 | const init_inst = try expr(mod, scope, result_loc, init_node); |
| 226 | 226 | const sub_scope = try block_arena.create(Scope.LocalPtr); |
| ... | ... | @@ -232,7 +232,7 @@ fn varDecl( |
| 232 | 232 | }; |
| 233 | 233 | return &sub_scope.base; |
| 234 | 234 | } else { |
| 235 | const alloc = try mod.addZIRNoOp(scope, name_src, .alloc_inferred); | |
| 235 | const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred); | |
| 236 | 236 | const result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? }; |
| 237 | 237 | const init_inst = try expr(mod, scope, result_loc, init_node); |
| 238 | 238 | const sub_scope = try block_arena.create(Scope.LocalPtr); |
| ... | ... | @@ -269,26 +269,26 @@ fn assignOp( |
| 269 | 269 | op_inst_tag: zir.Inst.Tag, |
| 270 | 270 | ) InnerError!void { |
| 271 | 271 | const lhs_ptr = try expr(mod, scope, .lvalue, infix_node.lhs); |
| 272 | const lhs = try mod.addZIRUnOp(scope, lhs_ptr.src, .deref, lhs_ptr); | |
| 273 | const lhs_type = try mod.addZIRUnOp(scope, lhs_ptr.src, .typeof, lhs); | |
| 272 | const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr); | |
| 273 | const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs); | |
| 274 | 274 | const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs); |
| 275 | 275 | |
| 276 | 276 | const tree = scope.tree(); |
| 277 | 277 | const src = tree.token_locs[infix_node.op_token].start; |
| 278 | 278 | |
| 279 | const result = try mod.addZIRBinOp(scope, src, op_inst_tag, lhs, rhs); | |
| 280 | _ = try mod.addZIRBinOp(scope, src, .store, lhs_ptr, result); | |
| 279 | const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); | |
| 280 | _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result); | |
| 281 | 281 | } |
| 282 | 282 | |
| 283 | 283 | fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { |
| 284 | 284 | const tree = scope.tree(); |
| 285 | 285 | const src = tree.token_locs[node.op_token].start; |
| 286 | const bool_type = try mod.addZIRInstConst(scope, src, .{ | |
| 286 | const bool_type = try addZIRInstConst(mod, scope, src, .{ | |
| 287 | 287 | .ty = Type.initTag(.type), |
| 288 | 288 | .val = Value.initTag(.bool_type), |
| 289 | 289 | }); |
| 290 | 290 | const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs); |
| 291 | return mod.addZIRUnOp(scope, src, .boolnot, operand); | |
| 291 | return addZIRUnOp(mod, scope, src, .boolnot, operand); | |
| 292 | 292 | } |
| 293 | 293 | |
| 294 | 294 | /// Identifier token -> String (allocated in scope.arena()) |
| ... | ... | @@ -317,7 +317,7 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToke |
| 317 | 317 | |
| 318 | 318 | const ident_name = try identifierTokenString(mod, scope, node.token); |
| 319 | 319 | |
| 320 | return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{}); | |
| 320 | return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{}); | |
| 321 | 321 | } |
| 322 | 322 | |
| 323 | 323 | fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { |
| ... | ... | @@ -328,15 +328,15 @@ fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError! |
| 328 | 328 | const lhs = try expr(mod, scope, .none, node.lhs); |
| 329 | 329 | const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?); |
| 330 | 330 | |
| 331 | const pointer = try mod.addZIRInst(scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}); | |
| 332 | return mod.addZIRUnOp(scope, src, .deref, pointer); | |
| 331 | const pointer = try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}); | |
| 332 | return addZIRUnOp(mod, scope, src, .deref, pointer); | |
| 333 | 333 | } |
| 334 | 334 | |
| 335 | 335 | fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst { |
| 336 | 336 | const tree = scope.tree(); |
| 337 | 337 | const src = tree.token_locs[node.rtoken].start; |
| 338 | 338 | const lhs = try expr(mod, scope, .none, node.lhs); |
| 339 | return mod.addZIRUnOp(scope, src, .deref, lhs); | |
| 339 | return addZIRUnOp(mod, scope, src, .deref, lhs); | |
| 340 | 340 | } |
| 341 | 341 | |
| 342 | 342 | fn simpleBinOp( |
| ... | ... | @@ -352,7 +352,7 @@ fn simpleBinOp( |
| 352 | 352 | const lhs = try expr(mod, scope, .none, infix_node.lhs); |
| 353 | 353 | const rhs = try expr(mod, scope, .none, infix_node.rhs); |
| 354 | 354 | |
| 355 | const result = try mod.addZIRBinOp(scope, src, op_inst_tag, lhs, rhs); | |
| 355 | const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); | |
| 356 | 356 | return rlWrap(mod, scope, rl, result); |
| 357 | 357 | } |
| 358 | 358 | |
| ... | ... | @@ -375,19 +375,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 375 | 375 | |
| 376 | 376 | const tree = scope.tree(); |
| 377 | 377 | const if_src = tree.token_locs[if_node.if_token].start; |
| 378 | const bool_type = try mod.addZIRInstConst(scope, if_src, .{ | |
| 378 | const bool_type = try addZIRInstConst(mod, scope, if_src, .{ | |
| 379 | 379 | .ty = Type.initTag(.type), |
| 380 | 380 | .val = Value.initTag(.bool_type), |
| 381 | 381 | }); |
| 382 | 382 | const cond = try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_node.condition); |
| 383 | 383 | |
| 384 | const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{ | |
| 384 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{ | |
| 385 | 385 | .condition = cond, |
| 386 | 386 | .then_body = undefined, // populated below |
| 387 | 387 | .else_body = undefined, // populated below |
| 388 | 388 | }, .{}); |
| 389 | 389 | |
| 390 | const block = try mod.addZIRInstBlock(scope, if_src, .{ | |
| 390 | const block = try addZIRInstBlock(mod, scope, if_src, .{ | |
| 391 | 391 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), |
| 392 | 392 | }); |
| 393 | 393 | var then_scope: Scope.GenZIR = .{ |
| ... | ... | @@ -410,7 +410,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 410 | 410 | const then_result = try expr(mod, &then_scope.base, branch_rl, if_node.body); |
| 411 | 411 | if (!then_result.tag.isNoReturn()) { |
| 412 | 412 | const then_src = tree.token_locs[if_node.body.lastToken()].start; |
| 413 | _ = try mod.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{ | |
| 413 | _ = try addZIRInst(mod, &then_scope.base, then_src, zir.Inst.Break, .{ | |
| 414 | 414 | .block = block, |
| 415 | 415 | .operand = then_result, |
| 416 | 416 | }, .{}); |
| ... | ... | @@ -431,7 +431,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 431 | 431 | const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body); |
| 432 | 432 | if (!else_result.tag.isNoReturn()) { |
| 433 | 433 | const else_src = tree.token_locs[else_node.body.lastToken()].start; |
| 434 | _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{ | |
| 434 | _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{ | |
| 435 | 435 | .block = block, |
| 436 | 436 | .operand = else_result, |
| 437 | 437 | }, .{}); |
| ... | ... | @@ -440,7 +440,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn |
| 440 | 440 | // TODO Optimization opportunity: we can avoid an allocation and a memcpy here |
| 441 | 441 | // by directly allocating the body for this one instruction. |
| 442 | 442 | const else_src = tree.token_locs[if_node.lastToken()].start; |
| 443 | _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.BreakVoid, .{ | |
| 443 | _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{ | |
| 444 | 444 | .block = block, |
| 445 | 445 | }, .{}); |
| 446 | 446 | } |
| ... | ... | @@ -456,16 +456,16 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE |
| 456 | 456 | const src = tree.token_locs[cfe.ltoken].start; |
| 457 | 457 | if (cfe.getRHS()) |rhs_node| { |
| 458 | 458 | if (nodeMayNeedMemoryLocation(rhs_node)) { |
| 459 | const ret_ptr = try mod.addZIRNoOp(scope, src, .ret_ptr); | |
| 459 | const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr); | |
| 460 | 460 | const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node); |
| 461 | return mod.addZIRUnOp(scope, src, .@"return", operand); | |
| 461 | return addZIRUnOp(mod, scope, src, .@"return", operand); | |
| 462 | 462 | } else { |
| 463 | const fn_ret_ty = try mod.addZIRNoOp(scope, src, .ret_type); | |
| 463 | const fn_ret_ty = try addZIRNoOp(mod, scope, src, .ret_type); | |
| 464 | 464 | const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node); |
| 465 | return mod.addZIRUnOp(scope, src, .@"return", operand); | |
| 465 | return addZIRUnOp(mod, scope, src, .@"return", operand); | |
| 466 | 466 | } |
| 467 | 467 | } else { |
| 468 | return mod.addZIRNoOp(scope, src, .returnvoid); | |
| 468 | return addZIRNoOp(mod, scope, src, .returnvoid); | |
| 469 | 469 | } |
| 470 | 470 | } |
| 471 | 471 | |
| ... | ... | @@ -481,7 +481,7 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError |
| 481 | 481 | } |
| 482 | 482 | |
| 483 | 483 | if (getSimplePrimitiveValue(ident_name)) |typed_value| { |
| 484 | return mod.addZIRInstConst(scope, src, typed_value); | |
| 484 | return addZIRInstConst(mod, scope, src, typed_value); | |
| 485 | 485 | } |
| 486 | 486 | |
| 487 | 487 | if (ident_name.len >= 2) integer: { |
| ... | ... | @@ -505,13 +505,13 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError |
| 505 | 505 | else => { |
| 506 | 506 | const int_type_payload = try scope.arena().create(Value.Payload.IntType); |
| 507 | 507 | int_type_payload.* = .{ .signed = is_signed, .bits = bit_count }; |
| 508 | return mod.addZIRInstConst(scope, src, .{ | |
| 508 | return addZIRInstConst(mod, scope, src, .{ | |
| 509 | 509 | .ty = Type.initTag(.comptime_int), |
| 510 | 510 | .val = Value.initPayload(&int_type_payload.base), |
| 511 | 511 | }); |
| 512 | 512 | }, |
| 513 | 513 | }; |
| 514 | return mod.addZIRInstConst(scope, src, .{ | |
| 514 | return addZIRInstConst(mod, scope, src, .{ | |
| 515 | 515 | .ty = Type.initTag(.type), |
| 516 | 516 | .val = val, |
| 517 | 517 | }); |
| ... | ... | @@ -532,7 +532,7 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError |
| 532 | 532 | .local_ptr => { |
| 533 | 533 | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 534 | 534 | if (mem.eql(u8, local_ptr.name, ident_name)) { |
| 535 | return try mod.addZIRUnOp(scope, src, .deref, local_ptr.ptr); | |
| 535 | return try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr); | |
| 536 | 536 | } |
| 537 | 537 | s = local_ptr.parent; |
| 538 | 538 | }, |
| ... | ... | @@ -542,7 +542,7 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError |
| 542 | 542 | } |
| 543 | 543 | |
| 544 | 544 | if (mod.lookupDeclName(scope, ident_name)) |decl| { |
| 545 | return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}); | |
| 545 | return try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}); | |
| 546 | 546 | } |
| 547 | 547 | |
| 548 | 548 | return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name}); |
| ... | ... | @@ -564,7 +564,7 @@ fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) Inner |
| 564 | 564 | }; |
| 565 | 565 | |
| 566 | 566 | const src = tree.token_locs[str_lit.token].start; |
| 567 | return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{}); | |
| 567 | return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{}); | |
| 568 | 568 | } |
| 569 | 569 | |
| 570 | 570 | fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst { |
| ... | ... | @@ -589,7 +589,7 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) Inne |
| 589 | 589 | const int_payload = try arena.create(Value.Payload.Int_u64); |
| 590 | 590 | int_payload.* = .{ .int = small_int }; |
| 591 | 591 | const src = tree.token_locs[int_lit.token].start; |
| 592 | return mod.addZIRInstConst(scope, src, .{ | |
| 592 | return addZIRInstConst(mod, scope, src, .{ | |
| 593 | 593 | .ty = Type.initTag(.comptime_int), |
| 594 | 594 | .val = Value.initPayload(&int_payload.base), |
| 595 | 595 | }); |
| ... | ... | @@ -612,7 +612,7 @@ fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) Inne |
| 612 | 612 | const float_payload = try arena.create(Value.Payload.Float_128); |
| 613 | 613 | float_payload.* = .{ .val = val }; |
| 614 | 614 | const src = tree.token_locs[float_lit.token].start; |
| 615 | return mod.addZIRInstConst(scope, src, .{ | |
| 615 | return addZIRInstConst(mod, scope, src, .{ | |
| 616 | 616 | .ty = Type.initTag(.comptime_float), |
| 617 | 617 | .val = Value.initPayload(&float_payload.base), |
| 618 | 618 | }); |
| ... | ... | @@ -622,7 +622,7 @@ fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerErro |
| 622 | 622 | const arena = scope.arena(); |
| 623 | 623 | const tree = scope.tree(); |
| 624 | 624 | const src = tree.token_locs[node.token].start; |
| 625 | return mod.addZIRInstConst(scope, src, .{ | |
| 625 | return addZIRInstConst(mod, scope, src, .{ | |
| 626 | 626 | .ty = Type.initTag(.@"undefined"), |
| 627 | 627 | .val = Value.initTag(.undef), |
| 628 | 628 | }); |
| ... | ... | @@ -632,7 +632,7 @@ fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError |
| 632 | 632 | const arena = scope.arena(); |
| 633 | 633 | const tree = scope.tree(); |
| 634 | 634 | const src = tree.token_locs[node.token].start; |
| 635 | return mod.addZIRInstConst(scope, src, .{ | |
| 635 | return addZIRInstConst(mod, scope, src, .{ | |
| 636 | 636 | .ty = Type.initTag(.bool), |
| 637 | 637 | .val = switch (tree.token_ids[node.token]) { |
| 638 | 638 | .Keyword_true => Value.initTag(.bool_true), |
| ... | ... | @@ -646,7 +646,7 @@ fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError |
| 646 | 646 | const arena = scope.arena(); |
| 647 | 647 | const tree = scope.tree(); |
| 648 | 648 | const src = tree.token_locs[node.token].start; |
| 649 | return mod.addZIRInstConst(scope, src, .{ | |
| 649 | return addZIRInstConst(mod, scope, src, .{ | |
| 650 | 650 | .ty = Type.initTag(.@"null"), |
| 651 | 651 | .val = Value.initTag(.null_value), |
| 652 | 652 | }); |
| ... | ... | @@ -664,7 +664,7 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi |
| 664 | 664 | |
| 665 | 665 | const src = tree.token_locs[asm_node.asm_token].start; |
| 666 | 666 | |
| 667 | const str_type = try mod.addZIRInstConst(scope, src, .{ | |
| 667 | const str_type = try addZIRInstConst(mod, scope, src, .{ | |
| 668 | 668 | .ty = Type.initTag(.type), |
| 669 | 669 | .val = Value.initTag(.const_slice_u8_type), |
| 670 | 670 | }); |
| ... | ... | @@ -676,11 +676,11 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi |
| 676 | 676 | args[i] = try expr(mod, scope, .none, input.expr); |
| 677 | 677 | } |
| 678 | 678 | |
| 679 | const return_type = try mod.addZIRInstConst(scope, src, .{ | |
| 679 | const return_type = try addZIRInstConst(mod, scope, src, .{ | |
| 680 | 680 | .ty = Type.initTag(.type), |
| 681 | 681 | .val = Value.initTag(.void_type), |
| 682 | 682 | }); |
| 683 | const asm_inst = try mod.addZIRInst(scope, src, zir.Inst.Asm, .{ | |
| 683 | const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{ | |
| 684 | 684 | .asm_source = try expr(mod, scope, str_type_rl, asm_node.template), |
| 685 | 685 | .return_type = return_type, |
| 686 | 686 | }, .{ |
| ... | ... | @@ -710,14 +710,14 @@ fn simpleCast( |
| 710 | 710 | try ensureBuiltinParamCount(mod, scope, call, 2); |
| 711 | 711 | const tree = scope.tree(); |
| 712 | 712 | const src = tree.token_locs[call.builtin_token].start; |
| 713 | const type_type = try mod.addZIRInstConst(scope, src, .{ | |
| 713 | const type_type = try addZIRInstConst(mod, scope, src, .{ | |
| 714 | 714 | .ty = Type.initTag(.type), |
| 715 | 715 | .val = Value.initTag(.type_type), |
| 716 | 716 | }); |
| 717 | 717 | const params = call.params(); |
| 718 | 718 | const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]); |
| 719 | 719 | const rhs = try expr(mod, scope, .none, params[1]); |
| 720 | const result = try mod.addZIRBinOp(scope, src, inst_tag, dest_type, rhs); | |
| 720 | const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs); | |
| 721 | 721 | return rlWrap(mod, scope, rl, result); |
| 722 | 722 | } |
| 723 | 723 | |
| ... | ... | @@ -726,7 +726,7 @@ fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError |
| 726 | 726 | const operand = try expr(mod, scope, .none, call.params()[0]); |
| 727 | 727 | const tree = scope.tree(); |
| 728 | 728 | const src = tree.token_locs[call.builtin_token].start; |
| 729 | return mod.addZIRUnOp(scope, src, .ptrtoint, operand); | |
| 729 | return addZIRUnOp(mod, scope, src, .ptrtoint, operand); | |
| 730 | 730 | } |
| 731 | 731 | |
| 732 | 732 | fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { |
| ... | ... | @@ -739,19 +739,19 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I |
| 739 | 739 | .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]), |
| 740 | 740 | .discard => { |
| 741 | 741 | const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]); |
| 742 | _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result); | |
| 742 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); | |
| 743 | 743 | return result; |
| 744 | 744 | }, |
| 745 | 745 | .lvalue => { |
| 746 | 746 | const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]); |
| 747 | return mod.addZIRUnOp(scope, result.src, .ref, result); | |
| 747 | return addZIRUnOp(mod, scope, result.src, .ref, result); | |
| 748 | 748 | }, |
| 749 | 749 | .ty => |result_ty| { |
| 750 | 750 | const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]); |
| 751 | return mod.addZIRBinOp(scope, src, .as, result_ty, result); | |
| 751 | return addZIRBinOp(mod, scope, src, .as, result_ty, result); | |
| 752 | 752 | }, |
| 753 | 753 | .ptr => |result_ptr| { |
| 754 | const casted_result_ptr = try mod.addZIRBinOp(scope, src, .coerce_result_ptr, dest_type, result_ptr); | |
| 754 | const casted_result_ptr = try addZIRBinOp(mod, scope, src, .coerce_result_ptr, dest_type, result_ptr); | |
| 755 | 755 | return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]); |
| 756 | 756 | }, |
| 757 | 757 | .bitcasted_ptr => |bitcasted_ptr| { |
| ... | ... | @@ -763,7 +763,7 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I |
| 763 | 763 | return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{}); |
| 764 | 764 | }, |
| 765 | 765 | .block_ptr => |block_ptr| { |
| 766 | const casted_block_ptr = try mod.addZIRInst(scope, src, zir.Inst.CoerceResultBlockPtr, .{ | |
| 766 | const casted_block_ptr = try addZIRInst(mod, scope, src, zir.Inst.CoerceResultBlockPtr, .{ | |
| 767 | 767 | .dest_type = dest_type, |
| 768 | 768 | .block = block_ptr, |
| 769 | 769 | }, .{}); |
| ... | ... | @@ -776,7 +776,7 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa |
| 776 | 776 | try ensureBuiltinParamCount(mod, scope, call, 2); |
| 777 | 777 | const tree = scope.tree(); |
| 778 | 778 | const src = tree.token_locs[call.builtin_token].start; |
| 779 | const type_type = try mod.addZIRInstConst(scope, src, .{ | |
| 779 | const type_type = try addZIRInstConst(mod, scope, src, .{ | |
| 780 | 780 | .ty = Type.initTag(.type), |
| 781 | 781 | .val = Value.initTag(.type_type), |
| 782 | 782 | }); |
| ... | ... | @@ -785,26 +785,26 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa |
| 785 | 785 | switch (rl) { |
| 786 | 786 | .none => { |
| 787 | 787 | const operand = try expr(mod, scope, .none, params[1]); |
| 788 | return mod.addZIRBinOp(scope, src, .bitcast, dest_type, operand); | |
| 788 | return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand); | |
| 789 | 789 | }, |
| 790 | 790 | .discard => { |
| 791 | 791 | const operand = try expr(mod, scope, .none, params[1]); |
| 792 | const result = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, operand); | |
| 793 | _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result); | |
| 792 | const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand); | |
| 793 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); | |
| 794 | 794 | return result; |
| 795 | 795 | }, |
| 796 | 796 | .lvalue => { |
| 797 | 797 | const operand = try expr(mod, scope, .lvalue, params[1]); |
| 798 | const result = try mod.addZIRBinOp(scope, src, .bitcast_lvalue, dest_type, operand); | |
| 798 | const result = try addZIRBinOp(mod, scope, src, .bitcast_lvalue, dest_type, operand); | |
| 799 | 799 | return result; |
| 800 | 800 | }, |
| 801 | 801 | .ty => |result_ty| { |
| 802 | 802 | const result = try expr(mod, scope, .none, params[1]); |
| 803 | const bitcasted = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, result); | |
| 804 | return mod.addZIRBinOp(scope, src, .as, result_ty, bitcasted); | |
| 803 | const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result); | |
| 804 | return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted); | |
| 805 | 805 | }, |
| 806 | 806 | .ptr => |result_ptr| { |
| 807 | const casted_result_ptr = try mod.addZIRUnOp(scope, src, .bitcast_result_ptr, result_ptr); | |
| 807 | const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr); | |
| 808 | 808 | return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]); |
| 809 | 809 | }, |
| 810 | 810 | .bitcasted_ptr => |bitcasted_ptr| { |
| ... | ... | @@ -852,7 +852,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In |
| 852 | 852 | const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len); |
| 853 | 853 | for (param_nodes) |param_node, i| { |
| 854 | 854 | const param_src = tree.token_locs[param_node.firstToken()].start; |
| 855 | const param_type = try mod.addZIRInst(scope, param_src, zir.Inst.ParamType, .{ | |
| 855 | const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{ | |
| 856 | 856 | .func = lhs, |
| 857 | 857 | .arg_index = i, |
| 858 | 858 | }, .{}); |
| ... | ... | @@ -860,7 +860,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In |
| 860 | 860 | } |
| 861 | 861 | |
| 862 | 862 | const src = tree.token_locs[node.lhs.firstToken()].start; |
| 863 | const result = try mod.addZIRInst(scope, src, zir.Inst.Call, .{ | |
| 863 | const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{ | |
| 864 | 864 | .func = lhs, |
| 865 | 865 | .args = args, |
| 866 | 866 | }, .{}); |
| ... | ... | @@ -871,7 +871,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In |
| 871 | 871 | fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst { |
| 872 | 872 | const tree = scope.tree(); |
| 873 | 873 | const src = tree.token_locs[unreach_node.token].start; |
| 874 | return mod.addZIRNoOp(scope, src, .@"unreachable"); | |
| 874 | return addZIRNoOp(mod, scope, src, .@"unreachable"); | |
| 875 | 875 | } |
| 876 | 876 | |
| 877 | 877 | fn getSimplePrimitiveValue(name: []const u8) ?TypedValue { |
| ... | ... | @@ -1053,20 +1053,20 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr |
| 1053 | 1053 | .none => return result, |
| 1054 | 1054 | .discard => { |
| 1055 | 1055 | // Emit a compile error for discarding error values. |
| 1056 | _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result); | |
| 1056 | _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result); | |
| 1057 | 1057 | return result; |
| 1058 | 1058 | }, |
| 1059 | 1059 | .lvalue => { |
| 1060 | 1060 | // We need a pointer but we have a value. |
| 1061 | return mod.addZIRUnOp(scope, result.src, .ref, result); | |
| 1061 | return addZIRUnOp(mod, scope, result.src, .ref, result); | |
| 1062 | 1062 | }, |
| 1063 | .ty => |ty_inst| return mod.addZIRBinOp(scope, result.src, .as, ty_inst, result), | |
| 1063 | .ty => |ty_inst| return addZIRBinOp(mod, scope, result.src, .as, ty_inst, result), | |
| 1064 | 1064 | .ptr => |ptr_inst| { |
| 1065 | const casted_result = try mod.addZIRInst(scope, result.src, zir.Inst.CoerceToPtrElem, .{ | |
| 1065 | const casted_result = try addZIRInst(mod, scope, result.src, zir.Inst.CoerceToPtrElem, .{ | |
| 1066 | 1066 | .ptr = ptr_inst, |
| 1067 | 1067 | .value = result, |
| 1068 | 1068 | }, .{}); |
| 1069 | _ = try mod.addZIRInst(scope, result.src, zir.Inst.Store, .{ | |
| 1069 | _ = try addZIRInst(mod, scope, result.src, zir.Inst.Store, .{ | |
| 1070 | 1070 | .ptr = ptr_inst, |
| 1071 | 1071 | .value = casted_result, |
| 1072 | 1072 | }, .{}); |
| ... | ... | @@ -1083,3 +1083,121 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr |
| 1083 | 1083 | }, |
| 1084 | 1084 | } |
| 1085 | 1085 | } |
| 1086 | ||
| 1087 | pub fn addZIRInstSpecial( | |
| 1088 | mod: *Module, | |
| 1089 | scope: *Scope, | |
| 1090 | src: usize, | |
| 1091 | comptime T: type, | |
| 1092 | positionals: std.meta.fieldInfo(T, "positionals").field_type, | |
| 1093 | kw_args: std.meta.fieldInfo(T, "kw_args").field_type, | |
| 1094 | ) !*T { | |
| 1095 | const gen_zir = scope.getGenZIR(); | |
| 1096 | try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1); | |
| 1097 | const inst = try gen_zir.arena.create(T); | |
| 1098 | inst.* = .{ | |
| 1099 | .base = .{ | |
| 1100 | .tag = T.base_tag, | |
| 1101 | .src = src, | |
| 1102 | }, | |
| 1103 | .positionals = positionals, | |
| 1104 | .kw_args = kw_args, | |
| 1105 | }; | |
| 1106 | gen_zir.instructions.appendAssumeCapacity(&inst.base); | |
| 1107 | return inst; | |
| 1108 | } | |
| 1109 | ||
| 1110 | pub fn addZIRNoOpT(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp { | |
| 1111 | const gen_zir = scope.getGenZIR(); | |
| 1112 | try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1); | |
| 1113 | const inst = try gen_zir.arena.create(zir.Inst.NoOp); | |
| 1114 | inst.* = .{ | |
| 1115 | .base = .{ | |
| 1116 | .tag = tag, | |
| 1117 | .src = src, | |
| 1118 | }, | |
| 1119 | .positionals = .{}, | |
| 1120 | .kw_args = .{}, | |
| 1121 | }; | |
| 1122 | gen_zir.instructions.appendAssumeCapacity(&inst.base); | |
| 1123 | return inst; | |
| 1124 | } | |
| 1125 | ||
| 1126 | pub fn addZIRNoOp(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst { | |
| 1127 | const inst = try addZIRNoOpT(mod, scope, src, tag); | |
| 1128 | return &inst.base; | |
| 1129 | } | |
| 1130 | ||
| 1131 | pub fn addZIRUnOp( | |
| 1132 | mod: *Module, | |
| 1133 | scope: *Scope, | |
| 1134 | src: usize, | |
| 1135 | tag: zir.Inst.Tag, | |
| 1136 | operand: *zir.Inst, | |
| 1137 | ) !*zir.Inst { | |
| 1138 | const gen_zir = scope.getGenZIR(); | |
| 1139 | try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1); | |
| 1140 | const inst = try gen_zir.arena.create(zir.Inst.UnOp); | |
| 1141 | inst.* = .{ | |
| 1142 | .base = .{ | |
| 1143 | .tag = tag, | |
| 1144 | .src = src, | |
| 1145 | }, | |
| 1146 | .positionals = .{ | |
| 1147 | .operand = operand, | |
| 1148 | }, | |
| 1149 | .kw_args = .{}, | |
| 1150 | }; | |
| 1151 | gen_zir.instructions.appendAssumeCapacity(&inst.base); | |
| 1152 | return &inst.base; | |
| 1153 | } | |
| 1154 | ||
| 1155 | pub fn addZIRBinOp( | |
| 1156 | mod: *Module, | |
| 1157 | scope: *Scope, | |
| 1158 | src: usize, | |
| 1159 | tag: zir.Inst.Tag, | |
| 1160 | lhs: *zir.Inst, | |
| 1161 | rhs: *zir.Inst, | |
| 1162 | ) !*zir.Inst { | |
| 1163 | const gen_zir = scope.getGenZIR(); | |
| 1164 | try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1); | |
| 1165 | const inst = try gen_zir.arena.create(zir.Inst.BinOp); | |
| 1166 | inst.* = .{ | |
| 1167 | .base = .{ | |
| 1168 | .tag = tag, | |
| 1169 | .src = src, | |
| 1170 | }, | |
| 1171 | .positionals = .{ | |
| 1172 | .lhs = lhs, | |
| 1173 | .rhs = rhs, | |
| 1174 | }, | |
| 1175 | .kw_args = .{}, | |
| 1176 | }; | |
| 1177 | gen_zir.instructions.appendAssumeCapacity(&inst.base); | |
| 1178 | return &inst.base; | |
| 1179 | } | |
| 1180 | ||
| 1181 | pub fn addZIRInst( | |
| 1182 | mod: *Module, | |
| 1183 | scope: *Scope, | |
| 1184 | src: usize, | |
| 1185 | comptime T: type, | |
| 1186 | positionals: std.meta.fieldInfo(T, "positionals").field_type, | |
| 1187 | kw_args: std.meta.fieldInfo(T, "kw_args").field_type, | |
| 1188 | ) !*zir.Inst { | |
| 1189 | const inst_special = try addZIRInstSpecial(mod, scope, src, T, positionals, kw_args); | |
| 1190 | return &inst_special.base; | |
| 1191 | } | |
| 1192 | ||
| 1193 | /// TODO The existence of this function is a workaround for a bug in stage1. | |
| 1194 | pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst { | |
| 1195 | const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type; | |
| 1196 | return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{}); | |
| 1197 | } | |
| 1198 | ||
| 1199 | /// TODO The existence of this function is a workaround for a bug in stage1. | |
| 1200 | pub fn addZIRInstBlock(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block { | |
| 1201 | const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type; | |
| 1202 | return addZIRInstSpecial(mod, scope, src, zir.Inst.Block, P{ .body = body }, .{}); | |
| 1203 | } |
src-self-hosted/zir_sema.zig created+1128| ... | ... | @@ -0,0 +1,1128 @@ |
| 1 | //! Semantic analysis of ZIR instructions. | |
| 2 | //! This file operates on a `Module` instance, transforming untyped ZIR | |
| 3 | //! instructions into semantically-analyzed IR instructions. It does type | |
| 4 | //! checking, comptime control flow, and safety-check generation. This is the | |
| 5 | //! the heart of the Zig compiler. | |
| 6 | //! When deciding if something goes into this file or into Module, here is a | |
| 7 | //! guiding principle: if it has to do with (untyped) ZIR instructions, it goes | |
| 8 | //! here. If the analysis operates on typed IR instructions, it goes in Module. | |
| 9 | ||
| 10 | const std = @import("std"); | |
| 11 | const mem = std.mem; | |
| 12 | const Allocator = std.mem.Allocator; | |
| 13 | const Value = @import("value.zig").Value; | |
| 14 | const Type = @import("type.zig").Type; | |
| 15 | const TypedValue = @import("TypedValue.zig"); | |
| 16 | const assert = std.debug.assert; | |
| 17 | const ir = @import("ir.zig"); | |
| 18 | const zir = @import("zir.zig"); | |
| 19 | const Module = @import("Module.zig"); | |
| 20 | const Inst = ir.Inst; | |
| 21 | const Body = ir.Body; | |
| 22 | const trace = @import("tracy.zig").trace; | |
| 23 | const Scope = Module.Scope; | |
| 24 | const InnerError = Module.InnerError; | |
| 25 | const Decl = Module.Decl; | |
| 26 | ||
| 27 | pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { | |
| 28 | switch (old_inst.tag) { | |
| 29 | .alloc => return analyzeInstAlloc(mod, scope, old_inst.castTag(.alloc).?), | |
| 30 | .alloc_inferred => return analyzeInstAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred).?), | |
| 31 | .arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?), | |
| 32 | .bitcast_lvalue => return analyzeInstBitCastLValue(mod, scope, old_inst.castTag(.bitcast_lvalue).?), | |
| 33 | .bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?), | |
| 34 | .block => return analyzeInstBlock(mod, scope, old_inst.castTag(.block).?), | |
| 35 | .@"break" => return analyzeInstBreak(mod, scope, old_inst.castTag(.@"break").?), | |
| 36 | .breakpoint => return analyzeInstBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?), | |
| 37 | .breakvoid => return analyzeInstBreakVoid(mod, scope, old_inst.castTag(.breakvoid).?), | |
| 38 | .call => return analyzeInstCall(mod, scope, old_inst.castTag(.call).?), | |
| 39 | .coerce_result_block_ptr => return analyzeInstCoerceResultBlockPtr(mod, scope, old_inst.castTag(.coerce_result_block_ptr).?), | |
| 40 | .coerce_result_ptr => return analyzeInstCoerceResultPtr(mod, scope, old_inst.castTag(.coerce_result_ptr).?), | |
| 41 | .coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?), | |
| 42 | .compileerror => return analyzeInstCompileError(mod, scope, old_inst.castTag(.compileerror).?), | |
| 43 | .@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?), | |
| 44 | .declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?), | |
| 45 | .declref_str => return analyzeInstDeclRefStr(mod, scope, old_inst.castTag(.declref_str).?), | |
| 46 | .declval => return analyzeInstDeclVal(mod, scope, old_inst.castTag(.declval).?), | |
| 47 | .declval_in_module => return analyzeInstDeclValInModule(mod, scope, old_inst.castTag(.declval_in_module).?), | |
| 48 | .ensure_result_used => return analyzeInstEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?), | |
| 49 | .ensure_result_non_error => return analyzeInstEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?), | |
| 50 | .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?), | |
| 51 | .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?), | |
| 52 | .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?), | |
| 53 | .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?), | |
| 54 | .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?), | |
| 55 | .int => { | |
| 56 | const big_int = old_inst.castTag(.int).?.positionals.int; | |
| 57 | return mod.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int); | |
| 58 | }, | |
| 59 | .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?), | |
| 60 | .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?), | |
| 61 | .ptrtoint => return analyzeInstPtrToInt(mod, scope, old_inst.castTag(.ptrtoint).?), | |
| 62 | .fieldptr => return analyzeInstFieldPtr(mod, scope, old_inst.castTag(.fieldptr).?), | |
| 63 | .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?), | |
| 64 | .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?), | |
| 65 | .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?), | |
| 66 | .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?), | |
| 67 | .unreach_nocheck => return analyzeInstUnreachNoChk(mod, scope, old_inst.castTag(.unreach_nocheck).?), | |
| 68 | .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?), | |
| 69 | .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?), | |
| 70 | .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?), | |
| 71 | .@"export" => return analyzeInstExport(mod, scope, old_inst.castTag(.@"export").?), | |
| 72 | .primitive => return analyzeInstPrimitive(mod, scope, old_inst.castTag(.primitive).?), | |
| 73 | .fntype => return analyzeInstFnType(mod, scope, old_inst.castTag(.fntype).?), | |
| 74 | .intcast => return analyzeInstIntCast(mod, scope, old_inst.castTag(.intcast).?), | |
| 75 | .bitcast => return analyzeInstBitCast(mod, scope, old_inst.castTag(.bitcast).?), | |
| 76 | .floatcast => return analyzeInstFloatCast(mod, scope, old_inst.castTag(.floatcast).?), | |
| 77 | .elemptr => return analyzeInstElemPtr(mod, scope, old_inst.castTag(.elemptr).?), | |
| 78 | .add => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.add).?), | |
| 79 | .addwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.addwrap).?), | |
| 80 | .sub => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.sub).?), | |
| 81 | .subwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.subwrap).?), | |
| 82 | .mul => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mul).?), | |
| 83 | .mulwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mulwrap).?), | |
| 84 | .div => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.div).?), | |
| 85 | .mod_rem => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mod_rem).?), | |
| 86 | .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?), | |
| 87 | .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?), | |
| 88 | .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?), | |
| 89 | .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?), | |
| 90 | .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?), | |
| 91 | .shl => return analyzeInstShl(mod, scope, old_inst.castTag(.shl).?), | |
| 92 | .shr => return analyzeInstShr(mod, scope, old_inst.castTag(.shr).?), | |
| 93 | .cmp_lt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_lt).?, .lt), | |
| 94 | .cmp_lte => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_lte).?, .lte), | |
| 95 | .cmp_eq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_eq).?, .eq), | |
| 96 | .cmp_gte => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gte).?, .gte), | |
| 97 | .cmp_gt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gt).?, .gt), | |
| 98 | .cmp_neq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_neq).?, .neq), | |
| 99 | .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?), | |
| 100 | .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true), | |
| 101 | .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false), | |
| 102 | .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), | |
| 103 | .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?), | |
| 104 | } | |
| 105 | } | |
| 106 | ||
| 107 | pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void { | |
| 108 | for (body.instructions) |src_inst| { | |
| 109 | src_inst.analyzed_inst = try analyzeInst(mod, scope, src_inst); | |
| 110 | } | |
| 111 | } | |
| 112 | ||
| 113 | pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type { | |
| 114 | try analyzeBody(mod, &block_scope.base, body); | |
| 115 | for (block_scope.instructions.items) |inst| { | |
| 116 | if (inst.castTag(.ret)) |ret| { | |
| 117 | const val = try mod.resolveConstValue(&block_scope.base, ret.operand); | |
| 118 | return val.toType(); | |
| 119 | } else { | |
| 120 | return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{}); | |
| 121 | } | |
| 122 | } | |
| 123 | unreachable; | |
| 124 | } | |
| 125 | ||
| 126 | pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool { | |
| 127 | var decl_scope: Scope.DeclAnalysis = .{ | |
| 128 | .decl = decl, | |
| 129 | .arena = std.heap.ArenaAllocator.init(mod.gpa), | |
| 130 | }; | |
| 131 | errdefer decl_scope.arena.deinit(); | |
| 132 | ||
| 133 | decl.analysis = .in_progress; | |
| 134 | ||
| 135 | const typed_value = try analyzeConstInst(mod, &decl_scope.base, src_decl.inst); | |
| 136 | const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); | |
| 137 | ||
| 138 | var prev_type_has_bits = false; | |
| 139 | var type_changed = true; | |
| 140 | ||
| 141 | if (decl.typedValueManaged()) |tvm| { | |
| 142 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); | |
| 143 | type_changed = !tvm.typed_value.ty.eql(typed_value.ty); | |
| 144 | ||
| 145 | tvm.deinit(mod.gpa); | |
| 146 | } | |
| 147 | ||
| 148 | arena_state.* = decl_scope.arena.state; | |
| 149 | decl.typed_value = .{ | |
| 150 | .most_recent = .{ | |
| 151 | .typed_value = typed_value, | |
| 152 | .arena = arena_state, | |
| 153 | }, | |
| 154 | }; | |
| 155 | decl.analysis = .complete; | |
| 156 | decl.generation = mod.generation; | |
| 157 | if (typed_value.ty.hasCodeGenBits()) { | |
| 158 | // We don't fully codegen the decl until later, but we do need to reserve a global | |
| 159 | // offset table index for it. This allows us to codegen decls out of dependency order, | |
| 160 | // increasing how many computations can be done in parallel. | |
| 161 | try mod.bin_file.allocateDeclIndexes(decl); | |
| 162 | try mod.work_queue.writeItem(.{ .codegen_decl = decl }); | |
| 163 | } else if (prev_type_has_bits) { | |
| 164 | mod.bin_file.freeDecl(decl); | |
| 165 | } | |
| 166 | ||
| 167 | return type_changed; | |
| 168 | } | |
| 169 | ||
| 170 | pub fn resolveZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl { | |
| 171 | const zir_module = mod.root_scope.cast(Scope.ZIRModule).?; | |
| 172 | const entry = zir_module.contents.module.findDecl(src_decl.name).?; | |
| 173 | return resolveZirDeclHavingIndex(mod, scope, src_decl, entry.index); | |
| 174 | } | |
| 175 | ||
| 176 | fn resolveZirDeclHavingIndex(mod: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl { | |
| 177 | const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name); | |
| 178 | const decl = mod.decl_table.get(name_hash).?; | |
| 179 | decl.src_index = src_index; | |
| 180 | try mod.ensureDeclAnalyzed(decl); | |
| 181 | return decl; | |
| 182 | } | |
| 183 | ||
| 184 | /// Declares a dependency on the decl. | |
| 185 | fn resolveCompleteZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl { | |
| 186 | const decl = try resolveZirDecl(mod, scope, src_decl); | |
| 187 | switch (decl.analysis) { | |
| 188 | .unreferenced => unreachable, | |
| 189 | .in_progress => unreachable, | |
| 190 | .outdated => unreachable, | |
| 191 | ||
| 192 | .dependency_failure, | |
| 193 | .sema_failure, | |
| 194 | .sema_failure_retryable, | |
| 195 | .codegen_failure, | |
| 196 | .codegen_failure_retryable, | |
| 197 | => return error.AnalysisFail, | |
| 198 | ||
| 199 | .complete => {}, | |
| 200 | } | |
| 201 | return decl; | |
| 202 | } | |
| 203 | ||
| 204 | /// TODO Look into removing this function. The body is only needed for .zir files, not .zig files. | |
| 205 | pub fn resolveInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { | |
| 206 | if (old_inst.analyzed_inst) |inst| return inst; | |
| 207 | ||
| 208 | // If this assert trips, the instruction that was referenced did not get properly | |
| 209 | // analyzed before it was referenced. | |
| 210 | const zir_module = scope.namespace().cast(Scope.ZIRModule).?; | |
| 211 | const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: { | |
| 212 | const decl_name = declval.positionals.name; | |
| 213 | const entry = zir_module.contents.module.findDecl(decl_name) orelse | |
| 214 | return mod.fail(scope, old_inst.src, "decl '{}' not found", .{decl_name}); | |
| 215 | break :blk entry; | |
| 216 | } else blk: { | |
| 217 | // If this assert trips, the instruction that was referenced did not get | |
| 218 | // properly analyzed by a previous instruction analysis before it was | |
| 219 | // referenced by the current one. | |
| 220 | break :blk zir_module.contents.module.findInstDecl(old_inst).?; | |
| 221 | }; | |
| 222 | const decl = try resolveCompleteZirDecl(mod, scope, entry.decl); | |
| 223 | const decl_ref = try mod.analyzeDeclRef(scope, old_inst.src, decl); | |
| 224 | // Note: it would be tempting here to store the result into old_inst.analyzed_inst field, | |
| 225 | // but this would prevent the analyzeDeclRef from happening, which is needed to properly | |
| 226 | // detect Decl dependencies and dependency failures on updates. | |
| 227 | return mod.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src); | |
| 228 | } | |
| 229 | ||
| 230 | fn resolveConstString(mod: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 { | |
| 231 | const new_inst = try resolveInst(mod, scope, old_inst); | |
| 232 | const wanted_type = Type.initTag(.const_slice_u8); | |
| 233 | const coerced_inst = try mod.coerce(scope, wanted_type, new_inst); | |
| 234 | const val = try mod.resolveConstValue(scope, coerced_inst); | |
| 235 | return val.toAllocatedBytes(scope.arena()); | |
| 236 | } | |
| 237 | ||
| 238 | fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type { | |
| 239 | const new_inst = try resolveInst(mod, scope, old_inst); | |
| 240 | const wanted_type = Type.initTag(.@"type"); | |
| 241 | const coerced_inst = try mod.coerce(scope, wanted_type, new_inst); | |
| 242 | const val = try mod.resolveConstValue(scope, coerced_inst); | |
| 243 | return val.toType(); | |
| 244 | } | |
| 245 | ||
| 246 | pub fn resolveInstConst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue { | |
| 247 | const new_inst = try resolveInst(mod, scope, old_inst); | |
| 248 | const val = try mod.resolveConstValue(scope, new_inst); | |
| 249 | return TypedValue{ | |
| 250 | .ty = new_inst.ty, | |
| 251 | .val = val, | |
| 252 | }; | |
| 253 | } | |
| 254 | ||
| 255 | fn analyzeInstConst(mod: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst { | |
| 256 | // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions | |
| 257 | // after analysis. | |
| 258 | const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena()); | |
| 259 | return mod.constInst(scope, const_inst.base.src, typed_value_copy); | |
| 260 | } | |
| 261 | ||
| 262 | fn analyzeConstInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue { | |
| 263 | const new_inst = try analyzeInst(mod, scope, old_inst); | |
| 264 | return TypedValue{ | |
| 265 | .ty = new_inst.ty, | |
| 266 | .val = try mod.resolveConstValue(scope, new_inst), | |
| 267 | }; | |
| 268 | } | |
| 269 | ||
| 270 | fn analyzeInstCoerceResultBlockPtr( | |
| 271 | mod: *Module, | |
| 272 | scope: *Scope, | |
| 273 | inst: *zir.Inst.CoerceResultBlockPtr, | |
| 274 | ) InnerError!*Inst { | |
| 275 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{}); | |
| 276 | } | |
| 277 | ||
| 278 | fn analyzeInstBitCastLValue(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 279 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastLValue", .{}); | |
| 280 | } | |
| 281 | ||
| 282 | fn analyzeInstBitCastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 283 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{}); | |
| 284 | } | |
| 285 | ||
| 286 | fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 287 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{}); | |
| 288 | } | |
| 289 | ||
| 290 | fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst { | |
| 291 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceToPtrElem", .{}); | |
| 292 | } | |
| 293 | ||
| 294 | fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 295 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstRetPtr", .{}); | |
| 296 | } | |
| 297 | ||
| 298 | fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 299 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstRef", .{}); | |
| 300 | } | |
| 301 | ||
| 302 | fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 303 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | |
| 304 | const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | |
| 305 | const ret_type = fn_ty.fnReturnType(); | |
| 306 | return mod.constType(scope, inst.base.src, ret_type); | |
| 307 | } | |
| 308 | ||
| 309 | fn analyzeInstEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 310 | const operand = try resolveInst(mod, scope, inst.positionals.operand); | |
| 311 | switch (operand.ty.zigTypeTag()) { | |
| 312 | .Void, .NoReturn => return mod.constVoid(scope, operand.src), | |
| 313 | else => return mod.fail(scope, operand.src, "expression value is ignored", .{}), | |
| 314 | } | |
| 315 | } | |
| 316 | ||
| 317 | fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 318 | const operand = try resolveInst(mod, scope, inst.positionals.operand); | |
| 319 | switch (operand.ty.zigTypeTag()) { | |
| 320 | .ErrorSet, .ErrorUnion => return mod.fail(scope, operand.src, "error is discarded", .{}), | |
| 321 | else => return mod.constVoid(scope, operand.src), | |
| 322 | } | |
| 323 | } | |
| 324 | ||
| 325 | fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 326 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAlloc", .{}); | |
| 327 | } | |
| 328 | ||
| 329 | fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 330 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{}); | |
| 331 | } | |
| 332 | ||
| 333 | fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.Store) InnerError!*Inst { | |
| 334 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstStore", .{}); | |
| 335 | } | |
| 336 | ||
| 337 | fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst { | |
| 338 | const fn_inst = try resolveInst(mod, scope, inst.positionals.func); | |
| 339 | const arg_index = inst.positionals.arg_index; | |
| 340 | ||
| 341 | const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) { | |
| 342 | .Fn => fn_inst.ty, | |
| 343 | .BoundFn => { | |
| 344 | return mod.fail(scope, fn_inst.src, "TODO implement analyzeInstParamType for method call syntax", .{}); | |
| 345 | }, | |
| 346 | else => { | |
| 347 | return mod.fail(scope, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty}); | |
| 348 | }, | |
| 349 | }; | |
| 350 | ||
| 351 | // TODO support C-style var args | |
| 352 | const param_count = fn_ty.fnParamLen(); | |
| 353 | if (arg_index >= param_count) { | |
| 354 | return mod.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} arguments", .{ | |
| 355 | arg_index, | |
| 356 | fn_ty, | |
| 357 | param_count, | |
| 358 | }); | |
| 359 | } | |
| 360 | ||
| 361 | // TODO support generic functions | |
| 362 | const param_type = fn_ty.fnParamType(arg_index); | |
| 363 | return mod.constType(scope, inst.base.src, param_type); | |
| 364 | } | |
| 365 | ||
| 366 | fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst { | |
| 367 | // The bytes references memory inside the ZIR module, which can get deallocated | |
| 368 | // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. | |
| 369 | var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); | |
| 370 | const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes); | |
| 371 | ||
| 372 | const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); | |
| 373 | ty_payload.* = .{ .len = arena_bytes.len }; | |
| 374 | ||
| 375 | const bytes_payload = try scope.arena().create(Value.Payload.Bytes); | |
| 376 | bytes_payload.* = .{ .data = arena_bytes }; | |
| 377 | ||
| 378 | const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{ | |
| 379 | .ty = Type.initPayload(&ty_payload.base), | |
| 380 | .val = Value.initPayload(&bytes_payload.base), | |
| 381 | }); | |
| 382 | return mod.analyzeDeclRef(scope, str_inst.base.src, new_decl); | |
| 383 | } | |
| 384 | ||
| 385 | fn analyzeInstExport(mod: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst { | |
| 386 | const symbol_name = try resolveConstString(mod, scope, export_inst.positionals.symbol_name); | |
| 387 | const exported_decl = mod.lookupDeclName(scope, export_inst.positionals.decl_name) orelse | |
| 388 | return mod.fail(scope, export_inst.base.src, "decl '{}' not found", .{export_inst.positionals.decl_name}); | |
| 389 | try mod.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl); | |
| 390 | return mod.constVoid(scope, export_inst.base.src); | |
| 391 | } | |
| 392 | ||
| 393 | fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst { | |
| 394 | return mod.fail(scope, inst.base.src, "{}", .{inst.positionals.msg}); | |
| 395 | } | |
| 396 | ||
| 397 | fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 398 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | |
| 399 | const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | |
| 400 | const param_index = b.instructions.items.len; | |
| 401 | const param_count = fn_ty.fnParamLen(); | |
| 402 | if (param_index >= param_count) { | |
| 403 | return mod.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{ | |
| 404 | param_index, | |
| 405 | param_count, | |
| 406 | }); | |
| 407 | } | |
| 408 | const param_type = fn_ty.fnParamType(param_index); | |
| 409 | return mod.addNoOp(b, inst.base.src, param_type, .arg); | |
| 410 | } | |
| 411 | ||
| 412 | fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst { | |
| 413 | const parent_block = scope.cast(Scope.Block).?; | |
| 414 | ||
| 415 | // Reserve space for a Block instruction so that generated Break instructions can | |
| 416 | // point to it, even if it doesn't end up getting used because the code ends up being | |
| 417 | // comptime evaluated. | |
| 418 | const block_inst = try parent_block.arena.create(Inst.Block); | |
| 419 | block_inst.* = .{ | |
| 420 | .base = .{ | |
| 421 | .tag = Inst.Block.base_tag, | |
| 422 | .ty = undefined, // Set after analysis. | |
| 423 | .src = inst.base.src, | |
| 424 | }, | |
| 425 | .body = undefined, | |
| 426 | }; | |
| 427 | ||
| 428 | var child_block: Scope.Block = .{ | |
| 429 | .parent = parent_block, | |
| 430 | .func = parent_block.func, | |
| 431 | .decl = parent_block.decl, | |
| 432 | .instructions = .{}, | |
| 433 | .arena = parent_block.arena, | |
| 434 | // TODO @as here is working around a miscompilation compiler bug :( | |
| 435 | .label = @as(?Scope.Block.Label, Scope.Block.Label{ | |
| 436 | .zir_block = inst, | |
| 437 | .results = .{}, | |
| 438 | .block_inst = block_inst, | |
| 439 | }), | |
| 440 | }; | |
| 441 | const label = &child_block.label.?; | |
| 442 | ||
| 443 | defer child_block.instructions.deinit(mod.gpa); | |
| 444 | defer label.results.deinit(mod.gpa); | |
| 445 | ||
| 446 | try analyzeBody(mod, &child_block.base, inst.positionals.body); | |
| 447 | ||
| 448 | // Blocks must terminate with noreturn instruction. | |
| 449 | assert(child_block.instructions.items.len != 0); | |
| 450 | assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn()); | |
| 451 | ||
| 452 | // Need to set the type and emit the Block instruction. This allows machine code generation | |
| 453 | // to emit a jump instruction to after the block when it encounters the break. | |
| 454 | try parent_block.instructions.append(mod.gpa, &block_inst.base); | |
| 455 | block_inst.base.ty = try mod.resolvePeerTypes(scope, label.results.items); | |
| 456 | block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; | |
| 457 | return &block_inst.base; | |
| 458 | } | |
| 459 | ||
| 460 | fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 461 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | |
| 462 | return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint); | |
| 463 | } | |
| 464 | ||
| 465 | fn analyzeInstBreak(mod: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst { | |
| 466 | const operand = try resolveInst(mod, scope, inst.positionals.operand); | |
| 467 | const block = inst.positionals.block; | |
| 468 | return analyzeBreak(mod, scope, inst.base.src, block, operand); | |
| 469 | } | |
| 470 | ||
| 471 | fn analyzeInstBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst { | |
| 472 | const block = inst.positionals.block; | |
| 473 | const void_inst = try mod.constVoid(scope, inst.base.src); | |
| 474 | return analyzeBreak(mod, scope, inst.base.src, block, void_inst); | |
| 475 | } | |
| 476 | ||
| 477 | fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst { | |
| 478 | const decl_name = try resolveConstString(mod, scope, inst.positionals.name); | |
| 479 | return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name); | |
| 480 | } | |
| 481 | ||
| 482 | fn analyzeInstDeclRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst { | |
| 483 | return mod.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name); | |
| 484 | } | |
| 485 | ||
| 486 | fn analyzeInstDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst { | |
| 487 | const decl = try analyzeDeclVal(mod, scope, inst); | |
| 488 | const ptr = try mod.analyzeDeclRef(scope, inst.base.src, decl); | |
| 489 | return mod.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); | |
| 490 | } | |
| 491 | ||
| 492 | fn analyzeInstDeclValInModule(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst { | |
| 493 | const decl = inst.positionals.decl; | |
| 494 | const ptr = try mod.analyzeDeclRef(scope, inst.base.src, decl); | |
| 495 | return mod.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); | |
| 496 | } | |
| 497 | ||
| 498 | fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { | |
| 499 | const func = try resolveInst(mod, scope, inst.positionals.func); | |
| 500 | if (func.ty.zigTypeTag() != .Fn) | |
| 501 | return mod.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty}); | |
| 502 | ||
| 503 | const cc = func.ty.fnCallingConvention(); | |
| 504 | if (cc == .Naked) { | |
| 505 | // TODO add error note: declared here | |
| 506 | return mod.fail( | |
| 507 | scope, | |
| 508 | inst.positionals.func.src, | |
| 509 | "unable to call function with naked calling convention", | |
| 510 | .{}, | |
| 511 | ); | |
| 512 | } | |
| 513 | const call_params_len = inst.positionals.args.len; | |
| 514 | const fn_params_len = func.ty.fnParamLen(); | |
| 515 | if (func.ty.fnIsVarArgs()) { | |
| 516 | if (call_params_len < fn_params_len) { | |
| 517 | // TODO add error note: declared here | |
| 518 | return mod.fail( | |
| 519 | scope, | |
| 520 | inst.positionals.func.src, | |
| 521 | "expected at least {} arguments, found {}", | |
| 522 | .{ fn_params_len, call_params_len }, | |
| 523 | ); | |
| 524 | } | |
| 525 | return mod.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{}); | |
| 526 | } else if (fn_params_len != call_params_len) { | |
| 527 | // TODO add error note: declared here | |
| 528 | return mod.fail( | |
| 529 | scope, | |
| 530 | inst.positionals.func.src, | |
| 531 | "expected {} arguments, found {}", | |
| 532 | .{ fn_params_len, call_params_len }, | |
| 533 | ); | |
| 534 | } | |
| 535 | ||
| 536 | if (inst.kw_args.modifier == .compile_time) { | |
| 537 | return mod.fail(scope, inst.base.src, "TODO implement comptime function calls", .{}); | |
| 538 | } | |
| 539 | if (inst.kw_args.modifier != .auto) { | |
| 540 | return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier}); | |
| 541 | } | |
| 542 | ||
| 543 | // TODO handle function calls of generic functions | |
| 544 | ||
| 545 | const fn_param_types = try mod.gpa.alloc(Type, fn_params_len); | |
| 546 | defer mod.gpa.free(fn_param_types); | |
| 547 | func.ty.fnParamTypes(fn_param_types); | |
| 548 | ||
| 549 | const casted_args = try scope.arena().alloc(*Inst, fn_params_len); | |
| 550 | for (inst.positionals.args) |src_arg, i| { | |
| 551 | const uncasted_arg = try resolveInst(mod, scope, src_arg); | |
| 552 | casted_args[i] = try mod.coerce(scope, fn_param_types[i], uncasted_arg); | |
| 553 | } | |
| 554 | ||
| 555 | const ret_type = func.ty.fnReturnType(); | |
| 556 | ||
| 557 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | |
| 558 | return mod.addCall(b, inst.base.src, ret_type, func, casted_args); | |
| 559 | } | |
| 560 | ||
| 561 | fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { | |
| 562 | const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type); | |
| 563 | const fn_zir = blk: { | |
| 564 | var fn_arena = std.heap.ArenaAllocator.init(mod.gpa); | |
| 565 | errdefer fn_arena.deinit(); | |
| 566 | ||
| 567 | const fn_zir = try scope.arena().create(Module.Fn.ZIR); | |
| 568 | fn_zir.* = .{ | |
| 569 | .body = .{ | |
| 570 | .instructions = fn_inst.positionals.body.instructions, | |
| 571 | }, | |
| 572 | .arena = fn_arena.state, | |
| 573 | }; | |
| 574 | break :blk fn_zir; | |
| 575 | }; | |
| 576 | const new_func = try scope.arena().create(Module.Fn); | |
| 577 | new_func.* = .{ | |
| 578 | .analysis = .{ .queued = fn_zir }, | |
| 579 | .owner_decl = scope.decl().?, | |
| 580 | }; | |
| 581 | const fn_payload = try scope.arena().create(Value.Payload.Function); | |
| 582 | fn_payload.* = .{ .func = new_func }; | |
| 583 | return mod.constInst(scope, fn_inst.base.src, .{ | |
| 584 | .ty = fn_type, | |
| 585 | .val = Value.initPayload(&fn_payload.base), | |
| 586 | }); | |
| 587 | } | |
| 588 | ||
| 589 | fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst { | |
| 590 | return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{}); | |
| 591 | } | |
| 592 | ||
| 593 | fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { | |
| 594 | const return_type = try resolveType(mod, scope, fntype.positionals.return_type); | |
| 595 | ||
| 596 | // Hot path for some common function types. | |
| 597 | if (fntype.positionals.param_types.len == 0) { | |
| 598 | if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Unspecified) { | |
| 599 | return mod.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args)); | |
| 600 | } | |
| 601 | ||
| 602 | if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .Unspecified) { | |
| 603 | return mod.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args)); | |
| 604 | } | |
| 605 | ||
| 606 | if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Naked) { | |
| 607 | return mod.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args)); | |
| 608 | } | |
| 609 | ||
| 610 | if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .C) { | |
| 611 | return mod.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args)); | |
| 612 | } | |
| 613 | } | |
| 614 | ||
| 615 | const arena = scope.arena(); | |
| 616 | const param_types = try arena.alloc(Type, fntype.positionals.param_types.len); | |
| 617 | for (fntype.positionals.param_types) |param_type, i| { | |
| 618 | param_types[i] = try resolveType(mod, scope, param_type); | |
| 619 | } | |
| 620 | ||
| 621 | const payload = try arena.create(Type.Payload.Function); | |
| 622 | payload.* = .{ | |
| 623 | .cc = fntype.kw_args.cc, | |
| 624 | .return_type = return_type, | |
| 625 | .param_types = param_types, | |
| 626 | }; | |
| 627 | return mod.constType(scope, fntype.base.src, Type.initPayload(&payload.base)); | |
| 628 | } | |
| 629 | ||
| 630 | fn analyzeInstPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { | |
| 631 | return mod.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue()); | |
| 632 | } | |
| 633 | ||
| 634 | fn analyzeInstAs(mod: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst { | |
| 635 | const dest_type = try resolveType(mod, scope, as.positionals.lhs); | |
| 636 | const new_inst = try resolveInst(mod, scope, as.positionals.rhs); | |
| 637 | return mod.coerce(scope, dest_type, new_inst); | |
| 638 | } | |
| 639 | ||
| 640 | fn analyzeInstPtrToInt(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst { | |
| 641 | const ptr = try resolveInst(mod, scope, ptrtoint.positionals.operand); | |
| 642 | if (ptr.ty.zigTypeTag() != .Pointer) { | |
| 643 | return mod.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty}); | |
| 644 | } | |
| 645 | // TODO handle known-pointer-address | |
| 646 | const b = try mod.requireRuntimeBlock(scope, ptrtoint.base.src); | |
| 647 | const ty = Type.initTag(.usize); | |
| 648 | return mod.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr); | |
| 649 | } | |
| 650 | ||
| 651 | fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst { | |
| 652 | const object_ptr = try resolveInst(mod, scope, fieldptr.positionals.object_ptr); | |
| 653 | const field_name = try resolveConstString(mod, scope, fieldptr.positionals.field_name); | |
| 654 | ||
| 655 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { | |
| 656 | .Pointer => object_ptr.ty.elemType(), | |
| 657 | else => return mod.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}), | |
| 658 | }; | |
| 659 | switch (elem_ty.zigTypeTag()) { | |
| 660 | .Array => { | |
| 661 | if (mem.eql(u8, field_name, "len")) { | |
| 662 | const len_payload = try scope.arena().create(Value.Payload.Int_u64); | |
| 663 | len_payload.* = .{ .int = elem_ty.arrayLen() }; | |
| 664 | ||
| 665 | const ref_payload = try scope.arena().create(Value.Payload.RefVal); | |
| 666 | ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) }; | |
| 667 | ||
| 668 | return mod.constInst(scope, fieldptr.base.src, .{ | |
| 669 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), | |
| 670 | .val = Value.initPayload(&ref_payload.base), | |
| 671 | }); | |
| 672 | } else { | |
| 673 | return mod.fail( | |
| 674 | scope, | |
| 675 | fieldptr.positionals.field_name.src, | |
| 676 | "no member named '{}' in '{}'", | |
| 677 | .{ field_name, elem_ty }, | |
| 678 | ); | |
| 679 | } | |
| 680 | }, | |
| 681 | else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}), | |
| 682 | } | |
| 683 | } | |
| 684 | ||
| 685 | fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 686 | const dest_type = try resolveType(mod, scope, inst.positionals.lhs); | |
| 687 | const operand = try resolveInst(mod, scope, inst.positionals.rhs); | |
| 688 | ||
| 689 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { | |
| 690 | .ComptimeInt => true, | |
| 691 | .Int => false, | |
| 692 | else => return mod.fail( | |
| 693 | scope, | |
| 694 | inst.positionals.lhs.src, | |
| 695 | "expected integer type, found '{}'", | |
| 696 | .{ | |
| 697 | dest_type, | |
| 698 | }, | |
| 699 | ), | |
| 700 | }; | |
| 701 | ||
| 702 | switch (operand.ty.zigTypeTag()) { | |
| 703 | .ComptimeInt, .Int => {}, | |
| 704 | else => return mod.fail( | |
| 705 | scope, | |
| 706 | inst.positionals.rhs.src, | |
| 707 | "expected integer type, found '{}'", | |
| 708 | .{operand.ty}, | |
| 709 | ), | |
| 710 | } | |
| 711 | ||
| 712 | if (operand.value() != null) { | |
| 713 | return mod.coerce(scope, dest_type, operand); | |
| 714 | } else if (dest_is_comptime_int) { | |
| 715 | return mod.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{}); | |
| 716 | } | |
| 717 | ||
| 718 | return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{}); | |
| 719 | } | |
| 720 | ||
| 721 | fn analyzeInstBitCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 722 | const dest_type = try resolveType(mod, scope, inst.positionals.lhs); | |
| 723 | const operand = try resolveInst(mod, scope, inst.positionals.rhs); | |
| 724 | return mod.bitcast(scope, dest_type, operand); | |
| 725 | } | |
| 726 | ||
| 727 | fn analyzeInstFloatCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 728 | const dest_type = try resolveType(mod, scope, inst.positionals.lhs); | |
| 729 | const operand = try resolveInst(mod, scope, inst.positionals.rhs); | |
| 730 | ||
| 731 | const dest_is_comptime_float = switch (dest_type.zigTypeTag()) { | |
| 732 | .ComptimeFloat => true, | |
| 733 | .Float => false, | |
| 734 | else => return mod.fail( | |
| 735 | scope, | |
| 736 | inst.positionals.lhs.src, | |
| 737 | "expected float type, found '{}'", | |
| 738 | .{ | |
| 739 | dest_type, | |
| 740 | }, | |
| 741 | ), | |
| 742 | }; | |
| 743 | ||
| 744 | switch (operand.ty.zigTypeTag()) { | |
| 745 | .ComptimeFloat, .Float, .ComptimeInt => {}, | |
| 746 | else => return mod.fail( | |
| 747 | scope, | |
| 748 | inst.positionals.rhs.src, | |
| 749 | "expected float type, found '{}'", | |
| 750 | .{operand.ty}, | |
| 751 | ), | |
| 752 | } | |
| 753 | ||
| 754 | if (operand.value() != null) { | |
| 755 | return mod.coerce(scope, dest_type, operand); | |
| 756 | } else if (dest_is_comptime_float) { | |
| 757 | return mod.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{}); | |
| 758 | } | |
| 759 | ||
| 760 | return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{}); | |
| 761 | } | |
| 762 | ||
| 763 | fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst { | |
| 764 | const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr); | |
| 765 | const uncasted_index = try resolveInst(mod, scope, inst.positionals.index); | |
| 766 | const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index); | |
| 767 | ||
| 768 | if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) { | |
| 769 | if (array_ptr.value()) |array_ptr_val| { | |
| 770 | if (elem_index.value()) |index_val| { | |
| 771 | // Both array pointer and index are compile-time known. | |
| 772 | const index_u64 = index_val.toUnsignedInt(); | |
| 773 | // @intCast here because it would have been impossible to construct a value that | |
| 774 | // required a larger index. | |
| 775 | const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64)); | |
| 776 | ||
| 777 | const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer); | |
| 778 | type_payload.* = .{ .pointee_type = array_ptr.ty.elemType().elemType() }; | |
| 779 | ||
| 780 | return mod.constInst(scope, inst.base.src, .{ | |
| 781 | .ty = Type.initPayload(&type_payload.base), | |
| 782 | .val = elem_ptr, | |
| 783 | }); | |
| 784 | } | |
| 785 | } | |
| 786 | } | |
| 787 | ||
| 788 | return mod.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{}); | |
| 789 | } | |
| 790 | ||
| 791 | fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 792 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{}); | |
| 793 | } | |
| 794 | ||
| 795 | fn analyzeInstShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 796 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShr", .{}); | |
| 797 | } | |
| 798 | ||
| 799 | fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 800 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{}); | |
| 801 | } | |
| 802 | ||
| 803 | fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 804 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{}); | |
| 805 | } | |
| 806 | ||
| 807 | fn analyzeInstArrayMul(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 808 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayMul", .{}); | |
| 809 | } | |
| 810 | ||
| 811 | fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | |
| 812 | const tracy = trace(@src()); | |
| 813 | defer tracy.end(); | |
| 814 | ||
| 815 | const lhs = try resolveInst(mod, scope, inst.positionals.lhs); | |
| 816 | const rhs = try resolveInst(mod, scope, inst.positionals.rhs); | |
| 817 | ||
| 818 | const instructions = &[_]*Inst{ lhs, rhs }; | |
| 819 | const resolved_type = try mod.resolvePeerTypes(scope, instructions); | |
| 820 | const casted_lhs = try mod.coerce(scope, resolved_type, lhs); | |
| 821 | const casted_rhs = try mod.coerce(scope, resolved_type, rhs); | |
| 822 | ||
| 823 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) | |
| 824 | resolved_type.elemType() | |
| 825 | else | |
| 826 | resolved_type; | |
| 827 | ||
| 828 | const scalar_tag = scalar_type.zigTypeTag(); | |
| 829 | ||
| 830 | if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { | |
| 831 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | |
| 832 | return mod.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{ | |
| 833 | lhs.ty.arrayLen(), | |
| 834 | rhs.ty.arrayLen(), | |
| 835 | }); | |
| 836 | } | |
| 837 | return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{}); | |
| 838 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { | |
| 839 | return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | |
| 840 | lhs.ty, | |
| 841 | rhs.ty, | |
| 842 | }); | |
| 843 | } | |
| 844 | ||
| 845 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; | |
| 846 | const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; | |
| 847 | ||
| 848 | if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) { | |
| 849 | return mod.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); | |
| 850 | } | |
| 851 | ||
| 852 | if (casted_lhs.value()) |lhs_val| { | |
| 853 | if (casted_rhs.value()) |rhs_val| { | |
| 854 | return analyzeInstComptimeOp(mod, scope, scalar_type, inst, lhs_val, rhs_val); | |
| 855 | } | |
| 856 | } | |
| 857 | ||
| 858 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | |
| 859 | const ir_tag = switch (inst.base.tag) { | |
| 860 | .add => Inst.Tag.add, | |
| 861 | .sub => Inst.Tag.sub, | |
| 862 | else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}), | |
| 863 | }; | |
| 864 | ||
| 865 | return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); | |
| 866 | } | |
| 867 | ||
| 868 | /// Analyzes operands that are known at comptime | |
| 869 | fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst { | |
| 870 | // incase rhs is 0, simply return lhs without doing any calculations | |
| 871 | // TODO Once division is implemented we should throw an error when dividing by 0. | |
| 872 | if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) { | |
| 873 | return mod.constInst(scope, inst.base.src, .{ | |
| 874 | .ty = res_type, | |
| 875 | .val = lhs_val, | |
| 876 | }); | |
| 877 | } | |
| 878 | const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt; | |
| 879 | ||
| 880 | const value = try switch (inst.base.tag) { | |
| 881 | .add => blk: { | |
| 882 | const val = if (is_int) | |
| 883 | Module.intAdd(scope.arena(), lhs_val, rhs_val) | |
| 884 | else | |
| 885 | mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val); | |
| 886 | break :blk val; | |
| 887 | }, | |
| 888 | .sub => blk: { | |
| 889 | const val = if (is_int) | |
| 890 | Module.intSub(scope.arena(), lhs_val, rhs_val) | |
| 891 | else | |
| 892 | mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val); | |
| 893 | break :blk val; | |
| 894 | }, | |
| 895 | else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}), | |
| 896 | }; | |
| 897 | ||
| 898 | return mod.constInst(scope, inst.base.src, .{ | |
| 899 | .ty = res_type, | |
| 900 | .val = value, | |
| 901 | }); | |
| 902 | } | |
| 903 | ||
| 904 | fn analyzeInstDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { | |
| 905 | const ptr = try resolveInst(mod, scope, deref.positionals.operand); | |
| 906 | return mod.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.operand.src); | |
| 907 | } | |
| 908 | ||
| 909 | fn analyzeInstAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst { | |
| 910 | const return_type = try resolveType(mod, scope, assembly.positionals.return_type); | |
| 911 | const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source); | |
| 912 | const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null; | |
| 913 | ||
| 914 | const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len); | |
| 915 | const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len); | |
| 916 | const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len); | |
| 917 | ||
| 918 | for (inputs) |*elem, i| { | |
| 919 | elem.* = try resolveConstString(mod, scope, assembly.kw_args.inputs[i]); | |
| 920 | } | |
| 921 | for (clobbers) |*elem, i| { | |
| 922 | elem.* = try resolveConstString(mod, scope, assembly.kw_args.clobbers[i]); | |
| 923 | } | |
| 924 | for (args) |*elem, i| { | |
| 925 | const arg = try resolveInst(mod, scope, assembly.kw_args.args[i]); | |
| 926 | elem.* = try mod.coerce(scope, Type.initTag(.usize), arg); | |
| 927 | } | |
| 928 | ||
| 929 | const b = try mod.requireRuntimeBlock(scope, assembly.base.src); | |
| 930 | const inst = try b.arena.create(Inst.Assembly); | |
| 931 | inst.* = .{ | |
| 932 | .base = .{ | |
| 933 | .tag = .assembly, | |
| 934 | .ty = return_type, | |
| 935 | .src = assembly.base.src, | |
| 936 | }, | |
| 937 | .asm_source = asm_source, | |
| 938 | .is_volatile = assembly.kw_args.@"volatile", | |
| 939 | .output = output, | |
| 940 | .inputs = inputs, | |
| 941 | .clobbers = clobbers, | |
| 942 | .args = args, | |
| 943 | }; | |
| 944 | try b.instructions.append(mod.gpa, &inst.base); | |
| 945 | return &inst.base; | |
| 946 | } | |
| 947 | ||
| 948 | fn analyzeInstCmp( | |
| 949 | mod: *Module, | |
| 950 | scope: *Scope, | |
| 951 | inst: *zir.Inst.BinOp, | |
| 952 | op: std.math.CompareOperator, | |
| 953 | ) InnerError!*Inst { | |
| 954 | const lhs = try resolveInst(mod, scope, inst.positionals.lhs); | |
| 955 | const rhs = try resolveInst(mod, scope, inst.positionals.rhs); | |
| 956 | ||
| 957 | const is_equality_cmp = switch (op) { | |
| 958 | .eq, .neq => true, | |
| 959 | else => false, | |
| 960 | }; | |
| 961 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | |
| 962 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | |
| 963 | if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) { | |
| 964 | // null == null, null != null | |
| 965 | return mod.constBool(scope, inst.base.src, op == .eq); | |
| 966 | } else if (is_equality_cmp and | |
| 967 | ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or | |
| 968 | rhs_ty_tag == .Null and lhs_ty_tag == .Optional)) | |
| 969 | { | |
| 970 | // comparing null with optionals | |
| 971 | const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; | |
| 972 | if (opt_operand.value()) |opt_val| { | |
| 973 | const is_null = opt_val.isNull(); | |
| 974 | return mod.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null); | |
| 975 | } | |
| 976 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | |
| 977 | const inst_tag: Inst.Tag = switch (op) { | |
| 978 | .eq => .isnull, | |
| 979 | .neq => .isnonnull, | |
| 980 | else => unreachable, | |
| 981 | }; | |
| 982 | return mod.addUnOp(b, inst.base.src, Type.initTag(.bool), inst_tag, opt_operand); | |
| 983 | } else if (is_equality_cmp and | |
| 984 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) | |
| 985 | { | |
| 986 | return mod.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{}); | |
| 987 | } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { | |
| 988 | const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty; | |
| 989 | return mod.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type}); | |
| 990 | } else if (is_equality_cmp and | |
| 991 | ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or | |
| 992 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) | |
| 993 | { | |
| 994 | return mod.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | |
| 995 | } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { | |
| 996 | if (!is_equality_cmp) { | |
| 997 | return mod.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)}); | |
| 998 | } | |
| 999 | return mod.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{}); | |
| 1000 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { | |
| 1001 | // This operation allows any combination of integer and float types, regardless of the | |
| 1002 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for | |
| 1003 | // numeric types. | |
| 1004 | return mod.cmpNumeric(scope, inst.base.src, lhs, rhs, op); | |
| 1005 | } | |
| 1006 | return mod.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{}); | |
| 1007 | } | |
| 1008 | ||
| 1009 | fn analyzeInstTypeOf(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 1010 | const operand = try resolveInst(mod, scope, inst.positionals.operand); | |
| 1011 | return mod.constType(scope, inst.base.src, operand.ty); | |
| 1012 | } | |
| 1013 | ||
| 1014 | fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 1015 | const uncasted_operand = try resolveInst(mod, scope, inst.positionals.operand); | |
| 1016 | const bool_type = Type.initTag(.bool); | |
| 1017 | const operand = try mod.coerce(scope, bool_type, uncasted_operand); | |
| 1018 | if (try mod.resolveDefinedValue(scope, operand)) |val| { | |
| 1019 | return mod.constBool(scope, inst.base.src, !val.toBool()); | |
| 1020 | } | |
| 1021 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | |
| 1022 | return mod.addUnOp(b, inst.base.src, bool_type, .not, operand); | |
| 1023 | } | |
| 1024 | ||
| 1025 | fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { | |
| 1026 | const operand = try resolveInst(mod, scope, inst.positionals.operand); | |
| 1027 | return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic); | |
| 1028 | } | |
| 1029 | ||
| 1030 | fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst { | |
| 1031 | const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition); | |
| 1032 | const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond); | |
| 1033 | ||
| 1034 | if (try mod.resolveDefinedValue(scope, cond)) |cond_val| { | |
| 1035 | const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body; | |
| 1036 | try analyzeBody(mod, scope, body.*); | |
| 1037 | return mod.constVoid(scope, inst.base.src); | |
| 1038 | } | |
| 1039 | ||
| 1040 | const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src); | |
| 1041 | ||
| 1042 | var true_block: Scope.Block = .{ | |
| 1043 | .parent = parent_block, | |
| 1044 | .func = parent_block.func, | |
| 1045 | .decl = parent_block.decl, | |
| 1046 | .instructions = .{}, | |
| 1047 | .arena = parent_block.arena, | |
| 1048 | }; | |
| 1049 | defer true_block.instructions.deinit(mod.gpa); | |
| 1050 | try analyzeBody(mod, &true_block.base, inst.positionals.then_body); | |
| 1051 | ||
| 1052 | var false_block: Scope.Block = .{ | |
| 1053 | .parent = parent_block, | |
| 1054 | .func = parent_block.func, | |
| 1055 | .decl = parent_block.decl, | |
| 1056 | .instructions = .{}, | |
| 1057 | .arena = parent_block.arena, | |
| 1058 | }; | |
| 1059 | defer false_block.instructions.deinit(mod.gpa); | |
| 1060 | try analyzeBody(mod, &false_block.base, inst.positionals.else_body); | |
| 1061 | ||
| 1062 | const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) }; | |
| 1063 | const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) }; | |
| 1064 | return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body); | |
| 1065 | } | |
| 1066 | ||
| 1067 | fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { | |
| 1068 | return mod.analyzeUnreach(scope, unreach.base.src); | |
| 1069 | } | |
| 1070 | ||
| 1071 | fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { | |
| 1072 | const b = try mod.requireRuntimeBlock(scope, unreach.base.src); | |
| 1073 | if (mod.wantSafety(scope)) { | |
| 1074 | // TODO Once we have a panic function to call, call it here instead of this. | |
| 1075 | _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint); | |
| 1076 | } | |
| 1077 | return mod.analyzeUnreach(scope, unreach.base.src); | |
| 1078 | } | |
| 1079 | ||
| 1080 | fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 1081 | const operand = try resolveInst(mod, scope, inst.positionals.operand); | |
| 1082 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | |
| 1083 | return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand); | |
| 1084 | } | |
| 1085 | ||
| 1086 | fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | |
| 1087 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | |
| 1088 | return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid); | |
| 1089 | } | |
| 1090 | ||
| 1091 | fn floatOpAllowed(tag: zir.Inst.Tag) bool { | |
| 1092 | // extend this swich as additional operators are implemented | |
| 1093 | return switch (tag) { | |
| 1094 | .add, .sub => true, | |
| 1095 | else => false, | |
| 1096 | }; | |
| 1097 | } | |
| 1098 | ||
| 1099 | fn analyzeBreak( | |
| 1100 | mod: *Module, | |
| 1101 | scope: *Scope, | |
| 1102 | src: usize, | |
| 1103 | zir_block: *zir.Inst.Block, | |
| 1104 | operand: *Inst, | |
| 1105 | ) InnerError!*Inst { | |
| 1106 | var opt_block = scope.cast(Scope.Block); | |
| 1107 | while (opt_block) |block| { | |
| 1108 | if (block.label) |*label| { | |
| 1109 | if (label.zir_block == zir_block) { | |
| 1110 | try label.results.append(mod.gpa, operand); | |
| 1111 | const b = try mod.requireRuntimeBlock(scope, src); | |
| 1112 | return mod.addBr(b, src, label.block_inst, operand); | |
| 1113 | } | |
| 1114 | } | |
| 1115 | opt_block = block.parent; | |
| 1116 | } else unreachable; | |
| 1117 | } | |
| 1118 | ||
| 1119 | fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl { | |
| 1120 | const decl_name = inst.positionals.name; | |
| 1121 | const zir_module = scope.namespace().cast(Scope.ZIRModule).?; | |
| 1122 | const src_decl = zir_module.contents.module.findDecl(decl_name) orelse | |
| 1123 | return mod.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name}); | |
| 1124 | ||
| 1125 | const decl = try resolveCompleteZirDecl(mod, scope, src_decl.decl); | |
| 1126 | ||
| 1127 | return decl; | |
| 1128 | } |