authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-27 22:44:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-27 22:44:18-07:00
log0965724e31666d156ca96375eee591380f3c9042
tree020b1a51091dfdcef21baeb26e83ca6ee503b9c1
parent5f0bde63582c800352b2d11e20bec650bd266a6f

self-hosted: refactor some code out of Module.zig into zir_sema.zig

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;
2020const trace = @import("tracy.zig").trace;
2121const liveness = @import("liveness.zig");
2222const astgen = @import("astgen.zig");
23const zir_sema = @import("zir_sema.zig");
2324
2425/// General-purpose allocator. Used for both temporary and long-term storage.
2526gpa: *Allocator,
......@@ -246,7 +247,7 @@ pub const Decl = struct {
246247 std.debug.warn("\n", .{});
247248 }
248249
249 fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
250 pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
250251 switch (self.typed_value) {
251252 .most_recent => |*x| return x,
252253 .never_succeeded => return null,
......@@ -1085,7 +1086,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
10851086 };
10861087}
10871088
1088fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
1089pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
10891090 const tracy = trace(@src());
10901091 defer tracy.end();
10911092
......@@ -1129,7 +1130,7 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
11291130 };
11301131
11311132 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])
11331134 else
11341135 self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
11351136 error.OutOfMemory => return error.OutOfMemory,
......@@ -1205,7 +1206,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12051206 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
12061207
12071208 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, .{
12091210 .ty = Type.initTag(.type),
12101211 .val = Value.initTag(.type_type),
12111212 });
......@@ -1244,11 +1245,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12441245 };
12451246
12461247 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, .{
12481249 .return_type = return_type_inst,
12491250 .param_types = param_types,
12501251 }, .{});
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);
12521253
12531254 // We need the memory for the Type to go into the arena for the Decl
12541255 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
......@@ -1264,7 +1265,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12641265 };
12651266 defer block_scope.instructions.deinit(self.gpa);
12661267
1267 const fn_type = try self.analyzeBodyValueAsType(&block_scope, .{
1268 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, .{
12681269 .instructions = fn_type_scope.instructions.items,
12691270 });
12701271 const new_func = try decl_arena.allocator.create(Fn);
......@@ -1317,7 +1318,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13171318 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))
13181319 {
13191320 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);
13211322 }
13221323
13231324 const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
......@@ -1387,19 +1388,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13871388 }
13881389}
13891390
1390fn 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
14031391fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
14041392 try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
14051393 try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
......@@ -1600,7 +1588,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
16001588 }
16011589 }
16021590 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);
16041592 }
16051593 // Handle explicitly deleted decls from the source code. Not to be confused
16061594 // with when we delete decls because they are no longer referenced.
......@@ -1705,7 +1693,7 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
17051693 func.analysis = .{ .in_progress = {} };
17061694 //std.debug.warn("set {} to in_progress\n", .{decl.name});
17071695
1708 try self.analyzeBody(&inner_block.base, fn_zir.body);
1696 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
17091697
17101698 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
17111699 func.analysis = .{ .success = .{ .instructions = instructions } };
......@@ -1758,131 +1746,18 @@ fn createNewDecl(
17581746 return new_decl;
17591747}
17601748
1761fn 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
1805fn 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
1811fn 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.
1820fn 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.
1840fn 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
18651749/// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites.
1866fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
1750pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
18671751 return scope.cast(Scope.Block) orelse
18681752 return self.fail(scope, src, "instruction illegal outside function body", .{});
18691753}
18701754
1871fn 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
1880fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
1755pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
18811756 return (try self.resolveDefinedValue(scope, base)) orelse
18821757 return self.fail(scope, base.src, "unable to resolve comptime value", .{});
18831758}
18841759
1885fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
1760pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
18861761 if (base.value()) |val| {
18871762 if (val.isUndef()) {
18881763 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 {
18921767 return null;
18931768}
18941769
1895fn 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
1903fn 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
1911fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const u8, exported_decl: *Decl) !void {
1770pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const u8, exported_decl: *Decl) !void {
19121771 try self.ensureDeclAnalyzed(exported_decl);
19131772 const typed_value = exported_decl.typed_value.most_recent.typed_value;
19141773 switch (typed_value.ty.zigTypeTag()) {
......@@ -1980,7 +1839,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
19801839 };
19811840}
19821841
1983fn addNoOp(
1842pub fn addNoOp(
19841843 self: *Module,
19851844 block: *Scope.Block,
19861845 src: usize,
......@@ -1999,7 +1858,7 @@ fn addNoOp(
19991858 return &inst.base;
20001859}
20011860
2002fn addUnOp(
1861pub fn addUnOp(
20031862 self: *Module,
20041863 block: *Scope.Block,
20051864 src: usize,
......@@ -2020,7 +1879,7 @@ fn addUnOp(
20201879 return &inst.base;
20211880}
20221881
2023fn addBinOp(
1882pub fn addBinOp(
20241883 self: *Module,
20251884 block: *Scope.Block,
20261885 src: usize,
......@@ -2043,7 +1902,7 @@ fn addBinOp(
20431902 return &inst.base;
20441903}
20451904
2046fn addBr(
1905pub fn addBr(
20471906 self: *Module,
20481907 scope_block: *Scope.Block,
20491908 src: usize,
......@@ -2064,7 +1923,7 @@ fn addBr(
20641923 return &inst.base;
20651924}
20661925
2067fn addCondBr(
1926pub fn addCondBr(
20681927 self: *Module,
20691928 block: *Scope.Block,
20701929 src: usize,
......@@ -2087,7 +1946,7 @@ fn addCondBr(
20871946 return &inst.base;
20881947}
20891948
2090fn addCall(
1949pub fn addCall(
20911950 self: *Module,
20921951 block: *Scope.Block,
20931952 src: usize,
......@@ -2109,138 +1968,7 @@ fn addCall(
21091968 return &inst.base;
21101969}
21111970
2112pub 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
2135pub 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
2151pub 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
2156pub 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
2180pub 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
2206pub 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.
2219pub 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.
2225pub 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
2230fn 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
2243fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
1971pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
22441972 const const_inst = try scope.arena().create(Inst.Constant);
22451973 const_inst.* = .{
22461974 .base = .{
......@@ -2253,42 +1981,42 @@ fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue)
22531981 return &const_inst.base;
22541982}
22551983
2256fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
1984pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
22571985 return self.constInst(scope, src, .{
22581986 .ty = Type.initTag(.type),
22591987 .val = try ty.toValue(scope.arena()),
22601988 });
22611989}
22621990
2263fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
1991pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
22641992 return self.constInst(scope, src, .{
22651993 .ty = Type.initTag(.void),
22661994 .val = Value.initTag(.the_one_possible_value),
22671995 });
22681996}
22691997
2270fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
1998pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
22711999 return self.constInst(scope, src, .{
22722000 .ty = Type.initTag(.noreturn),
22732001 .val = Value.initTag(.the_one_possible_value),
22742002 });
22752003}
22762004
2277fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
2005pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
22782006 return self.constInst(scope, src, .{
22792007 .ty = ty,
22802008 .val = Value.initTag(.undef),
22812009 });
22822010}
22832011
2284fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
2012pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
22852013 return self.constInst(scope, src, .{
22862014 .ty = Type.initTag(.bool),
22872015 .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
22882016 });
22892017}
22902018
2291fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
2019pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
22922020 const int_payload = try scope.arena().create(Value.Payload.Int_u64);
22932021 int_payload.* = .{ .int = int };
22942022
......@@ -2298,7 +2026,7 @@ fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64
22982026 });
22992027}
23002028
2301fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
2029pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
23022030 const int_payload = try scope.arena().create(Value.Payload.Int_i64);
23032031 int_payload.* = .{ .int = int };
23042032
......@@ -2308,7 +2036,7 @@ fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64)
23082036 });
23092037}
23102038
2311fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
2039pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
23122040 const val_payload = if (big_int.positive) blk: {
23132041 if (big_int.to(u64)) |x| {
23142042 return self.constIntUnsigned(scope, src, ty, x);
......@@ -2337,217 +2065,7 @@ fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigI
23372065 });
23382066}
23392067
2340fn 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
2348fn 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
2355fn 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
2435fn 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
2443fn analyzeInstBitCastLValue(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2444 return self.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastLValue", .{});
2445}
2446
2447fn analyzeInstBitCastResultPtr(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2448 return self.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{});
2449}
2450
2451fn analyzeInstCoerceResultPtr(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
2452 return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});
2453}
2454
2455fn analyzeInstCoerceToPtrElem(self: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst {
2456 return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceToPtrElem", .{});
2457}
2458
2459fn analyzeInstRetPtr(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
2460 return self.fail(scope, inst.base.src, "TODO implement analyzeInstRetPtr", .{});
2461}
2462
2463fn analyzeInstRef(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2464 return self.fail(scope, inst.base.src, "TODO implement analyzeInstRef", .{});
2465}
2466
2467fn 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
2474fn 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
2482fn 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
2490fn analyzeInstAlloc(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2491 return self.fail(scope, inst.base.src, "TODO implement analyzeInstAlloc", .{});
2492}
2493
2494fn analyzeInstAllocInferred(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
2495 return self.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
2496}
2497
2498fn analyzeInstStore(self: *Module, scope: *Scope, inst: *zir.Inst.Store) InnerError!*Inst {
2499 return self.fail(scope, inst.base.src, "TODO implement analyzeInstStore", .{});
2500}
2501
2502fn 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
2531fn 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
2550fn createAnonymousDecl(
2068pub fn createAnonymousDecl(
25512069 self: *Module,
25522070 scope: *Scope,
25532071 decl_arena: *std.heap.ArenaAllocator,
......@@ -2593,151 +2111,7 @@ pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*De
25932111 return self.decl_table.get(name_hash);
25942112}
25952113
2596fn 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
2604fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {
2605 return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
2606}
2607
2608fn 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
2623fn 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
2671fn 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
2676fn 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
2682fn 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
2688fn 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
2708fn 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
2713fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
2714 return self.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name);
2715}
2716
2717fn 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
2728fn 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
2734fn 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
2740fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
2114pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
27412115 const scope_decl = scope.decl().?;
27422116 try self.declareDeclDependency(scope_decl, decl);
27432117 self.ensureDeclAnalyzed(decl) catch |err| {
......@@ -2765,432 +2139,7 @@ fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerEr
27652139 });
27662140}
27672141
2768fn 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
2774fn 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
2837fn 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
2865fn analyzeInstIntType(self: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst {
2866 return self.fail(scope, inttype.base.src, "TODO implement inttype", .{});
2867}
2868
2869fn 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
2906fn 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
2910fn 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
2916fn 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
2927fn 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
2961fn 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
2997fn 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
3003fn 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
3039fn 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
3067fn 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
3075fn analyzeInstShl(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
3076 return self.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{});
3077}
3078
3079fn analyzeInstShr(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
3080 return self.fail(scope, inst.base.src, "TODO implement analyzeInstShr", .{});
3081}
3082
3083fn analyzeInstBitwise(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
3084 return self.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{});
3085}
3086
3087fn analyzeInstArrayCat(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
3088 return self.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{});
3089}
3090
3091fn analyzeInstArrayMul(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
3092 return self.fail(scope, inst.base.src, "TODO implement analyzeInstArrayMul", .{});
3093}
3094
3095fn 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
3153fn 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
3188fn 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
3193fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
2142pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
31942143 const elem_ty = switch (ptr.ty.zigTypeTag()) {
31952144 .Pointer => ptr.ty.elemType(),
31962145 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
32052154 return self.fail(scope, src, "TODO implement runtime deref", .{});
32062155}
32072156
3208fn 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
3247fn 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
3308fn 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
3313fn 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
3324fn 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
3329fn 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);
2157pub 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);
33642161}
33652162
3366fn wantSafety(self: *Module, scope: *Scope) bool {
2163pub fn wantSafety(self: *Module, scope: *Scope) bool {
33672164 return switch (self.optimize_mode) {
33682165 .Debug => true,
33692166 .ReleaseSafe => true,
......@@ -3372,42 +2169,12 @@ fn wantSafety(self: *Module, scope: *Scope) bool {
33722169 };
33732170}
33742171
3375fn analyzeUnreach(self: *Module, scope: *Scope, src: usize) InnerError!*Inst {
2172pub fn analyzeUnreach(self: *Module, scope: *Scope, src: usize) InnerError!*Inst {
33762173 const b = try self.requireRuntimeBlock(scope, src);
33772174 return self.addNoOp(b, src, Type.initTag(.noreturn), .unreach);
33782175}
33792176
3380fn analyzeInstUnreachNoChk(self: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {
3381 return self.analyzeUnreach(scope, unreach.base.src);
3382}
3383
3384fn 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
3393fn 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
3399fn 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
3404fn 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
3410fn analyzeIsNull(
2177pub fn analyzeIsNull(
34112178 self: *Module,
34122179 scope: *Scope,
34132180 src: usize,
......@@ -3418,7 +2185,7 @@ fn analyzeIsNull(
34182185}
34192186
34202187/// Asserts that lhs and rhs types are both numeric.
3421fn cmpNumeric(
2188pub fn cmpNumeric(
34222189 self: *Module,
34232190 scope: *Scope,
34242191 src: usize,
......@@ -3601,7 +2368,7 @@ fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
36012368 }
36022369}
36032370
3604fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
2371pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
36052372 if (instructions.len == 0)
36062373 return Type.initTag(.noreturn);
36072374
......@@ -3641,7 +2408,7 @@ fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
36412408 return prev_inst.ty;
36422409}
36432410
3644fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2411pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
36452412 // If the types are the same, we can return the operand.
36462413 if (dest_type.eql(inst.ty))
36472414 return inst;
......@@ -3737,7 +2504,7 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
37372504 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
37382505}
37392506
3740fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2507pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
37412508 if (inst.value()) |val| {
37422509 // Keep the comptime Value representation; take the new type.
37432510 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 {
38842651 return @bitCast(u128, a) == @bitCast(u128, b);
38852652}
38862653
3887fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
2654pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
38882655 // TODO is this a performance issue? maybe we should try the operation without
38892656 // resorting to BigInt first.
38902657 var lhs_space: Value.BigIntSpace = undefined;
......@@ -3912,7 +2679,7 @@ fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
39122679 return Value.initPayload(val_payload);
39132680}
39142681
3915fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
2682pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
39162683 // TODO is this a performance issue? maybe we should try the operation without
39172684 // resorting to BigInt first.
39182685 var lhs_space: Value.BigIntSpace = undefined;
......@@ -3940,7 +2707,7 @@ fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
39402707 return Value.initPayload(val_payload);
39412708}
39422709
3943fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinOp, lhs: Value, rhs: Value) !Value {
2710pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
39442711 var bit_count = switch (float_type.tag()) {
39452712 .comptime_float => 128,
39462713 else => float_type.floatBits(self.target()),
......@@ -3949,7 +2716,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
39492716 const allocator = scope.arena();
39502717 const val_payload = switch (bit_count) {
39512718 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", .{});
39532720 },
39542721 32 => blk: {
39552722 const lhs_val = lhs.toFloat(f32);
......@@ -3966,7 +2733,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
39662733 break :blk &val_payload.base;
39672734 },
39682735 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", .{});
39702737 },
39712738 else => unreachable,
39722739 };
......@@ -3974,7 +2741,7 @@ fn floatAdd(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
39742741 return Value.initPayload(val_payload);
39752742}
39762743
3977fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinOp, lhs: Value, rhs: Value) !Value {
2744pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
39782745 var bit_count = switch (float_type.tag()) {
39792746 .comptime_float => 128,
39802747 else => float_type.floatBits(self.target()),
......@@ -3983,7 +2750,7 @@ fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
39832750 const allocator = scope.arena();
39842751 const val_payload = switch (bit_count) {
39852752 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", .{});
39872754 },
39882755 32 => blk: {
39892756 const lhs_val = lhs.toFloat(f32);
......@@ -4000,7 +2767,7 @@ fn floatSub(self: *Module, scope: *Scope, float_type: Type, inst: *zir.Inst.BinO
40002767 break :blk &val_payload.base;
40012768 },
40022769 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", .{});
40042771 },
40052772 else => unreachable,
40062773 };
src-self-hosted/astgen.zig+184-66
......@@ -36,7 +36,7 @@ pub const ResultLoc = union(enum) {
3636
3737pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {
3838 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, .{
4040 .ty = Type.initTag(.type),
4141 .val = Value.initTag(.type_type),
4242 });
......@@ -146,7 +146,7 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
146146 else => {
147147 const possibly_unused_result = try expr(mod, scope, .none, statement);
148148 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);
150150 },
151151 }
152152 }
......@@ -177,7 +177,7 @@ fn varDecl(
177177 if (nodeMayNeedMemoryLocation(init_node)) {
178178 if (node.getTrailer("type_node")) |type_node| {
179179 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);
181181 const result_loc: ResultLoc = .{ .ptr = alloc };
182182 const init_inst = try expr(mod, scope, result_loc, init_node);
183183 const sub_scope = try block_arena.create(Scope.LocalVal);
......@@ -189,7 +189,7 @@ fn varDecl(
189189 };
190190 return &sub_scope.base;
191191 } else {
192 const alloc = try mod.addZIRNoOpT(scope, name_src, .alloc_inferred);
192 const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
193193 const result_loc: ResultLoc = .{ .inferred_ptr = alloc };
194194 const init_inst = try expr(mod, scope, result_loc, init_node);
195195 const sub_scope = try block_arena.create(Scope.LocalVal);
......@@ -220,7 +220,7 @@ fn varDecl(
220220 .Keyword_var => {
221221 if (node.getTrailer("type_node")) |type_node| {
222222 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);
224224 const result_loc: ResultLoc = .{ .ptr = alloc };
225225 const init_inst = try expr(mod, scope, result_loc, init_node);
226226 const sub_scope = try block_arena.create(Scope.LocalPtr);
......@@ -232,7 +232,7 @@ fn varDecl(
232232 };
233233 return &sub_scope.base;
234234 } else {
235 const alloc = try mod.addZIRNoOp(scope, name_src, .alloc_inferred);
235 const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
236236 const result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? };
237237 const init_inst = try expr(mod, scope, result_loc, init_node);
238238 const sub_scope = try block_arena.create(Scope.LocalPtr);
......@@ -269,26 +269,26 @@ fn assignOp(
269269 op_inst_tag: zir.Inst.Tag,
270270) InnerError!void {
271271 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);
274274 const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs);
275275
276276 const tree = scope.tree();
277277 const src = tree.token_locs[infix_node.op_token].start;
278278
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);
281281}
282282
283283fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
284284 const tree = scope.tree();
285285 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, .{
287287 .ty = Type.initTag(.type),
288288 .val = Value.initTag(.bool_type),
289289 });
290290 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);
292292}
293293
294294/// Identifier token -> String (allocated in scope.arena())
......@@ -317,7 +317,7 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToke
317317
318318 const ident_name = try identifierTokenString(mod, scope, node.token);
319319
320 return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
320 return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
321321}
322322
323323fn 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!
328328 const lhs = try expr(mod, scope, .none, node.lhs);
329329 const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
330330
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);
333333}
334334
335335fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
336336 const tree = scope.tree();
337337 const src = tree.token_locs[node.rtoken].start;
338338 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);
340340}
341341
342342fn simpleBinOp(
......@@ -352,7 +352,7 @@ fn simpleBinOp(
352352 const lhs = try expr(mod, scope, .none, infix_node.lhs);
353353 const rhs = try expr(mod, scope, .none, infix_node.rhs);
354354
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);
356356 return rlWrap(mod, scope, rl, result);
357357}
358358
......@@ -375,19 +375,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
375375
376376 const tree = scope.tree();
377377 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, .{
379379 .ty = Type.initTag(.type),
380380 .val = Value.initTag(.bool_type),
381381 });
382382 const cond = try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_node.condition);
383383
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, .{
385385 .condition = cond,
386386 .then_body = undefined, // populated below
387387 .else_body = undefined, // populated below
388388 }, .{});
389389
390 const block = try mod.addZIRInstBlock(scope, if_src, .{
390 const block = try addZIRInstBlock(mod, scope, if_src, .{
391391 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
392392 });
393393 var then_scope: Scope.GenZIR = .{
......@@ -410,7 +410,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
410410 const then_result = try expr(mod, &then_scope.base, branch_rl, if_node.body);
411411 if (!then_result.tag.isNoReturn()) {
412412 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, .{
414414 .block = block,
415415 .operand = then_result,
416416 }, .{});
......@@ -431,7 +431,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
431431 const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
432432 if (!else_result.tag.isNoReturn()) {
433433 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, .{
435435 .block = block,
436436 .operand = else_result,
437437 }, .{});
......@@ -440,7 +440,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
440440 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
441441 // by directly allocating the body for this one instruction.
442442 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, .{
444444 .block = block,
445445 }, .{});
446446 }
......@@ -456,16 +456,16 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE
456456 const src = tree.token_locs[cfe.ltoken].start;
457457 if (cfe.getRHS()) |rhs_node| {
458458 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);
460460 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);
462462 } 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);
464464 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);
466466 }
467467 } else {
468 return mod.addZIRNoOp(scope, src, .returnvoid);
468 return addZIRNoOp(mod, scope, src, .returnvoid);
469469 }
470470}
471471
......@@ -481,7 +481,7 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
481481 }
482482
483483 if (getSimplePrimitiveValue(ident_name)) |typed_value| {
484 return mod.addZIRInstConst(scope, src, typed_value);
484 return addZIRInstConst(mod, scope, src, typed_value);
485485 }
486486
487487 if (ident_name.len >= 2) integer: {
......@@ -505,13 +505,13 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
505505 else => {
506506 const int_type_payload = try scope.arena().create(Value.Payload.IntType);
507507 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
508 return mod.addZIRInstConst(scope, src, .{
508 return addZIRInstConst(mod, scope, src, .{
509509 .ty = Type.initTag(.comptime_int),
510510 .val = Value.initPayload(&int_type_payload.base),
511511 });
512512 },
513513 };
514 return mod.addZIRInstConst(scope, src, .{
514 return addZIRInstConst(mod, scope, src, .{
515515 .ty = Type.initTag(.type),
516516 .val = val,
517517 });
......@@ -532,7 +532,7 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
532532 .local_ptr => {
533533 const local_ptr = s.cast(Scope.LocalPtr).?;
534534 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);
536536 }
537537 s = local_ptr.parent;
538538 },
......@@ -542,7 +542,7 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
542542 }
543543
544544 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 }, .{});
546546 }
547547
548548 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
564564 };
565565
566566 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 }, .{});
568568}
569569
570570fn 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
589589 const int_payload = try arena.create(Value.Payload.Int_u64);
590590 int_payload.* = .{ .int = small_int };
591591 const src = tree.token_locs[int_lit.token].start;
592 return mod.addZIRInstConst(scope, src, .{
592 return addZIRInstConst(mod, scope, src, .{
593593 .ty = Type.initTag(.comptime_int),
594594 .val = Value.initPayload(&int_payload.base),
595595 });
......@@ -612,7 +612,7 @@ fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) Inne
612612 const float_payload = try arena.create(Value.Payload.Float_128);
613613 float_payload.* = .{ .val = val };
614614 const src = tree.token_locs[float_lit.token].start;
615 return mod.addZIRInstConst(scope, src, .{
615 return addZIRInstConst(mod, scope, src, .{
616616 .ty = Type.initTag(.comptime_float),
617617 .val = Value.initPayload(&float_payload.base),
618618 });
......@@ -622,7 +622,7 @@ fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerErro
622622 const arena = scope.arena();
623623 const tree = scope.tree();
624624 const src = tree.token_locs[node.token].start;
625 return mod.addZIRInstConst(scope, src, .{
625 return addZIRInstConst(mod, scope, src, .{
626626 .ty = Type.initTag(.@"undefined"),
627627 .val = Value.initTag(.undef),
628628 });
......@@ -632,7 +632,7 @@ fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError
632632 const arena = scope.arena();
633633 const tree = scope.tree();
634634 const src = tree.token_locs[node.token].start;
635 return mod.addZIRInstConst(scope, src, .{
635 return addZIRInstConst(mod, scope, src, .{
636636 .ty = Type.initTag(.bool),
637637 .val = switch (tree.token_ids[node.token]) {
638638 .Keyword_true => Value.initTag(.bool_true),
......@@ -646,7 +646,7 @@ fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError
646646 const arena = scope.arena();
647647 const tree = scope.tree();
648648 const src = tree.token_locs[node.token].start;
649 return mod.addZIRInstConst(scope, src, .{
649 return addZIRInstConst(mod, scope, src, .{
650650 .ty = Type.initTag(.@"null"),
651651 .val = Value.initTag(.null_value),
652652 });
......@@ -664,7 +664,7 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
664664
665665 const src = tree.token_locs[asm_node.asm_token].start;
666666
667 const str_type = try mod.addZIRInstConst(scope, src, .{
667 const str_type = try addZIRInstConst(mod, scope, src, .{
668668 .ty = Type.initTag(.type),
669669 .val = Value.initTag(.const_slice_u8_type),
670670 });
......@@ -676,11 +676,11 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
676676 args[i] = try expr(mod, scope, .none, input.expr);
677677 }
678678
679 const return_type = try mod.addZIRInstConst(scope, src, .{
679 const return_type = try addZIRInstConst(mod, scope, src, .{
680680 .ty = Type.initTag(.type),
681681 .val = Value.initTag(.void_type),
682682 });
683 const asm_inst = try mod.addZIRInst(scope, src, zir.Inst.Asm, .{
683 const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{
684684 .asm_source = try expr(mod, scope, str_type_rl, asm_node.template),
685685 .return_type = return_type,
686686 }, .{
......@@ -710,14 +710,14 @@ fn simpleCast(
710710 try ensureBuiltinParamCount(mod, scope, call, 2);
711711 const tree = scope.tree();
712712 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, .{
714714 .ty = Type.initTag(.type),
715715 .val = Value.initTag(.type_type),
716716 });
717717 const params = call.params();
718718 const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]);
719719 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);
721721 return rlWrap(mod, scope, rl, result);
722722}
723723
......@@ -726,7 +726,7 @@ fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError
726726 const operand = try expr(mod, scope, .none, call.params()[0]);
727727 const tree = scope.tree();
728728 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);
730730}
731731
732732fn 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
739739 .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]),
740740 .discard => {
741741 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);
743743 return result;
744744 },
745745 .lvalue => {
746746 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);
748748 },
749749 .ty => |result_ty| {
750750 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);
752752 },
753753 .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);
755755 return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]);
756756 },
757757 .bitcasted_ptr => |bitcasted_ptr| {
......@@ -763,7 +763,7 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I
763763 return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
764764 },
765765 .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, .{
767767 .dest_type = dest_type,
768768 .block = block_ptr,
769769 }, .{});
......@@ -776,7 +776,7 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
776776 try ensureBuiltinParamCount(mod, scope, call, 2);
777777 const tree = scope.tree();
778778 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, .{
780780 .ty = Type.initTag(.type),
781781 .val = Value.initTag(.type_type),
782782 });
......@@ -785,26 +785,26 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
785785 switch (rl) {
786786 .none => {
787787 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);
789789 },
790790 .discard => {
791791 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);
794794 return result;
795795 },
796796 .lvalue => {
797797 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);
799799 return result;
800800 },
801801 .ty => |result_ty| {
802802 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);
805805 },
806806 .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);
808808 return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
809809 },
810810 .bitcasted_ptr => |bitcasted_ptr| {
......@@ -852,7 +852,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
852852 const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
853853 for (param_nodes) |param_node, i| {
854854 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, .{
856856 .func = lhs,
857857 .arg_index = i,
858858 }, .{});
......@@ -860,7 +860,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
860860 }
861861
862862 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, .{
864864 .func = lhs,
865865 .args = args,
866866 }, .{});
......@@ -871,7 +871,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
871871fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
872872 const tree = scope.tree();
873873 const src = tree.token_locs[unreach_node.token].start;
874 return mod.addZIRNoOp(scope, src, .@"unreachable");
874 return addZIRNoOp(mod, scope, src, .@"unreachable");
875875}
876876
877877fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
......@@ -1053,20 +1053,20 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
10531053 .none => return result,
10541054 .discard => {
10551055 // 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);
10571057 return result;
10581058 },
10591059 .lvalue => {
10601060 // 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);
10621062 },
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),
10641064 .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, .{
10661066 .ptr = ptr_inst,
10671067 .value = result,
10681068 }, .{});
1069 _ = try mod.addZIRInst(scope, result.src, zir.Inst.Store, .{
1069 _ = try addZIRInst(mod, scope, result.src, zir.Inst.Store, .{
10701070 .ptr = ptr_inst,
10711071 .value = casted_result,
10721072 }, .{});
......@@ -1083,3 +1083,121 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
10831083 },
10841084 }
10851085}
1086
1087pub 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
1110pub 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
1126pub 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
1131pub 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
1155pub 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
1181pub 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.
1194pub 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.
1200pub 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
10const std = @import("std");
11const mem = std.mem;
12const Allocator = std.mem.Allocator;
13const Value = @import("value.zig").Value;
14const Type = @import("type.zig").Type;
15const TypedValue = @import("TypedValue.zig");
16const assert = std.debug.assert;
17const ir = @import("ir.zig");
18const zir = @import("zir.zig");
19const Module = @import("Module.zig");
20const Inst = ir.Inst;
21const Body = ir.Body;
22const trace = @import("tracy.zig").trace;
23const Scope = Module.Scope;
24const InnerError = Module.InnerError;
25const Decl = Module.Decl;
26
27pub 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
107pub 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
113pub 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
126pub 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
170pub 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
176fn 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.
185fn 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.
205pub 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
230fn 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
238fn 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
246pub 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
255fn 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
262fn 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
270fn 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
278fn analyzeInstBitCastLValue(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
279 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastLValue", .{});
280}
281
282fn analyzeInstBitCastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
283 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{});
284}
285
286fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
287 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});
288}
289
290fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst {
291 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceToPtrElem", .{});
292}
293
294fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
295 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstRetPtr", .{});
296}
297
298fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
299 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstRef", .{});
300}
301
302fn 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
309fn 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
317fn 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
325fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
326 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAlloc", .{});
327}
328
329fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
330 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
331}
332
333fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.Store) InnerError!*Inst {
334 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstStore", .{});
335}
336
337fn 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
366fn 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
385fn 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
393fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {
394 return mod.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
395}
396
397fn 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
412fn 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
460fn 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
465fn 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
471fn 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
477fn 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
482fn analyzeInstDeclRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
483 return mod.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name);
484}
485
486fn 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
492fn 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
498fn 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
561fn 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
589fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst {
590 return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{});
591}
592
593fn 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
630fn 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
634fn 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
640fn 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
651fn 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
685fn 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
721fn 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
727fn 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
763fn 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
791fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
792 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{});
793}
794
795fn analyzeInstShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
796 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShr", .{});
797}
798
799fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
800 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{});
801}
802
803fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
804 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{});
805}
806
807fn analyzeInstArrayMul(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
808 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayMul", .{});
809}
810
811fn 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
869fn 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
904fn 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
909fn 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
948fn 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
1009fn 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
1014fn 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
1025fn 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
1030fn 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
1067fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {
1068 return mod.analyzeUnreach(scope, unreach.base.src);
1069}
1070
1071fn 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
1080fn 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
1086fn 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
1091fn 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
1099fn 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
1119fn 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}