| author | |
| committer | |
| log | 62d27fcfb687e3ab1f10c72513e19529d8ffceed |
| tree | 2885da99ca325959e40f417346aca63ddef4fb31 |
| parent | 7935e83b1d5d29cca058597ebdac6dfd012a790a |
| parent | c2e66d9bab396a69514ec7c3c41fb0404e542f21 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
Stage2 inferred error sets and `@panic`17 files changed, 1074 insertions(+), 384 deletions(-)
lib/std/builtin.zig+7| ... | @@ -677,6 +677,13 @@ pub const panic: PanicFn = if (@hasDecl(root, "panic")) root.panic else default_ | ... | @@ -677,6 +677,13 @@ pub const panic: PanicFn = if (@hasDecl(root, "panic")) root.panic else default_ |
| 677 | /// therefore must be kept in sync with the compiler implementation. | 677 | /// therefore must be kept in sync with the compiler implementation. |
| 678 | pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn { | 678 | pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn { |
| 679 | @setCold(true); | 679 | @setCold(true); |
| 680 | // Until self-hosted catches up with stage1 language features, we have a simpler | ||
| 681 | // default panic function: | ||
| 682 | if (builtin.zig_is_stage2) { | ||
| 683 | while (true) { | ||
| 684 | @breakpoint(); | ||
| 685 | } | ||
| 686 | } | ||
| 680 | if (@hasDecl(root, "os") and @hasDecl(root.os, "panic")) { | 687 | if (@hasDecl(root, "os") and @hasDecl(root.os, "panic")) { |
| 681 | root.os.panic(msg, error_return_trace); | 688 | root.os.panic(msg, error_return_trace); |
| 682 | unreachable; | 689 | unreachable; |
lib/std/hash_map.zig+26-6| ... | @@ -483,10 +483,20 @@ pub fn HashMap( | ... | @@ -483,10 +483,20 @@ pub fn HashMap( |
| 483 | return self.unmanaged.getOrPutValueContext(self.allocator, key, value, self.ctx); | 483 | return self.unmanaged.getOrPutValueContext(self.allocator, key, value, self.ctx); |
| 484 | } | 484 | } |
| 485 | 485 | ||
| 486 | /// Deprecated: call `ensureUnusedCapacity` or `ensureTotalCapacity`. | ||
| 487 | pub const ensureCapacity = ensureTotalCapacity; | ||
| 488 | |||
| 486 | /// Increases capacity, guaranteeing that insertions up until the | 489 | /// Increases capacity, guaranteeing that insertions up until the |
| 487 | /// `expected_count` will not cause an allocation, and therefore cannot fail. | 490 | /// `expected_count` will not cause an allocation, and therefore cannot fail. |
| 488 | pub fn ensureCapacity(self: *Self, expected_count: Size) !void { | 491 | pub fn ensureTotalCapacity(self: *Self, expected_count: Size) !void { |
| 489 | return self.unmanaged.ensureCapacityContext(self.allocator, expected_count, self.ctx); | 492 | return self.unmanaged.ensureTotalCapacityContext(self.allocator, expected_count, self.ctx); |
| 493 | } | ||
| 494 | |||
| 495 | /// Increases capacity, guaranteeing that insertions up until | ||
| 496 | /// `additional_count` **more** items will not cause an allocation, and | ||
| 497 | /// therefore cannot fail. | ||
| 498 | pub fn ensureUnusedCapacity(self: *Self, additional_count: Size) !void { | ||
| 499 | return self.unmanaged.ensureUnusedCapacityContext(self.allocator, additional_count, self.ctx); | ||
| 490 | } | 500 | } |
| 491 | 501 | ||
| 492 | /// Returns the number of total elements which may be present before it is | 502 | /// Returns the number of total elements which may be present before it is |
| ... | @@ -821,16 +831,26 @@ pub fn HashMapUnmanaged( | ... | @@ -821,16 +831,26 @@ pub fn HashMapUnmanaged( |
| 821 | return new_cap; | 831 | return new_cap; |
| 822 | } | 832 | } |
| 823 | 833 | ||
| 824 | pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_size: Size) !void { | 834 | /// Deprecated: call `ensureUnusedCapacity` or `ensureTotalCapacity`. |
| 835 | pub const ensureCapacity = ensureTotalCapacity; | ||
| 836 | |||
| 837 | pub fn ensureTotalCapacity(self: *Self, allocator: *Allocator, new_size: Size) !void { | ||
| 825 | if (@sizeOf(Context) != 0) | 838 | if (@sizeOf(Context) != 0) |
| 826 | @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call ensureCapacityContext instead."); | 839 | @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call ensureTotalCapacityContext instead."); |
| 827 | return ensureCapacityContext(self, allocator, new_size, undefined); | 840 | return ensureTotalCapacityContext(self, allocator, new_size, undefined); |
| 828 | } | 841 | } |
| 829 | pub fn ensureCapacityContext(self: *Self, allocator: *Allocator, new_size: Size, ctx: Context) !void { | 842 | pub fn ensureTotalCapacityContext(self: *Self, allocator: *Allocator, new_size: Size, ctx: Context) !void { |
| 830 | if (new_size > self.size) | 843 | if (new_size > self.size) |
| 831 | try self.growIfNeeded(allocator, new_size - self.size, ctx); | 844 | try self.growIfNeeded(allocator, new_size - self.size, ctx); |
| 832 | } | 845 | } |
| 833 | 846 | ||
| 847 | pub fn ensureUnusedCapacity(self: *Self, allocator: *Allocator, additional_size: Size) !void { | ||
| 848 | return ensureUnusedCapacityContext(self, allocator, additional_size, undefined); | ||
| 849 | } | ||
| 850 | pub fn ensureUnusedCapacityContext(self: *Self, allocator: *Allocator, additional_size: Size, ctx: Context) !void { | ||
| 851 | return ensureTotalCapacityContext(self, allocator, self.capacity() + additional_size, ctx); | ||
| 852 | } | ||
| 853 | |||
| 834 | pub fn clearRetainingCapacity(self: *Self) void { | 854 | pub fn clearRetainingCapacity(self: *Self) void { |
| 835 | if (self.metadata) |_| { | 855 | if (self.metadata) |_| { |
| 836 | self.initMetadatas(); | 856 | self.initMetadatas(); |
src/AstGen.zig+164-57| ... | @@ -786,7 +786,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr | ... | @@ -786,7 +786,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr |
| 786 | rl, | 786 | rl, |
| 787 | node, | 787 | node, |
| 788 | node_datas[node].lhs, | 788 | node_datas[node].lhs, |
| 789 | .is_err_ptr, | 789 | .is_non_err_ptr, |
| 790 | .err_union_payload_unsafe_ptr, | 790 | .err_union_payload_unsafe_ptr, |
| 791 | .err_union_code_ptr, | 791 | .err_union_code_ptr, |
| 792 | node_datas[node].rhs, | 792 | node_datas[node].rhs, |
| ... | @@ -798,7 +798,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr | ... | @@ -798,7 +798,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr |
| 798 | rl, | 798 | rl, |
| 799 | node, | 799 | node, |
| 800 | node_datas[node].lhs, | 800 | node_datas[node].lhs, |
| 801 | .is_err, | 801 | .is_non_err, |
| 802 | .err_union_payload_unsafe, | 802 | .err_union_payload_unsafe, |
| 803 | .err_union_code, | 803 | .err_union_code, |
| 804 | node_datas[node].rhs, | 804 | node_datas[node].rhs, |
| ... | @@ -813,7 +813,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr | ... | @@ -813,7 +813,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr |
| 813 | rl, | 813 | rl, |
| 814 | node, | 814 | node, |
| 815 | node_datas[node].lhs, | 815 | node_datas[node].lhs, |
| 816 | .is_null_ptr, | 816 | .is_non_null_ptr, |
| 817 | .optional_payload_unsafe_ptr, | 817 | .optional_payload_unsafe_ptr, |
| 818 | undefined, | 818 | undefined, |
| 819 | node_datas[node].rhs, | 819 | node_datas[node].rhs, |
| ... | @@ -825,7 +825,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr | ... | @@ -825,7 +825,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr |
| 825 | rl, | 825 | rl, |
| 826 | node, | 826 | node, |
| 827 | node_datas[node].lhs, | 827 | node_datas[node].lhs, |
| 828 | .is_null, | 828 | .is_non_null, |
| 829 | .optional_payload_unsafe, | 829 | .optional_payload_unsafe, |
| 830 | undefined, | 830 | undefined, |
| 831 | node_datas[node].rhs, | 831 | node_datas[node].rhs, |
| ... | @@ -1860,7 +1860,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const ast.Nod | ... | @@ -1860,7 +1860,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const ast.Nod |
| 1860 | } | 1860 | } |
| 1861 | } | 1861 | } |
| 1862 | 1862 | ||
| 1863 | try genDefers(gz, parent_scope, scope, .none); | 1863 | try genDefers(gz, parent_scope, scope, .normal_only); |
| 1864 | try checkUsed(gz, parent_scope, scope); | 1864 | try checkUsed(gz, parent_scope, scope); |
| 1865 | } | 1865 | } |
| 1866 | 1866 | ||
| ... | @@ -1948,11 +1948,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner | ... | @@ -1948,11 +1948,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 1948 | .float128, | 1948 | .float128, |
| 1949 | .int_type, | 1949 | .int_type, |
| 1950 | .is_non_null, | 1950 | .is_non_null, |
| 1951 | .is_null, | ||
| 1952 | .is_non_null_ptr, | 1951 | .is_non_null_ptr, |
| 1953 | .is_null_ptr, | 1952 | .is_non_err, |
| 1954 | .is_err, | 1953 | .is_non_err_ptr, |
| 1955 | .is_err_ptr, | ||
| 1956 | .mod_rem, | 1954 | .mod_rem, |
| 1957 | .mul, | 1955 | .mul, |
| 1958 | .mulwrap, | 1956 | .mulwrap, |
| ... | @@ -2102,6 +2100,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner | ... | @@ -2102,6 +2100,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2102 | .@"resume", | 2100 | .@"resume", |
| 2103 | .@"await", | 2101 | .@"await", |
| 2104 | .await_nosuspend, | 2102 | .await_nosuspend, |
| 2103 | .ret_err_value_code, | ||
| 2105 | .extended, | 2104 | .extended, |
| 2106 | => break :b false, | 2105 | => break :b false, |
| 2107 | 2106 | ||
| ... | @@ -2113,6 +2112,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner | ... | @@ -2113,6 +2112,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2113 | .compile_error, | 2112 | .compile_error, |
| 2114 | .ret_node, | 2113 | .ret_node, |
| 2115 | .ret_coerce, | 2114 | .ret_coerce, |
| 2115 | .ret_err_value, | ||
| 2116 | .@"unreachable", | 2116 | .@"unreachable", |
| 2117 | .repeat, | 2117 | .repeat, |
| 2118 | .repeat_inline, | 2118 | .repeat_inline, |
| ... | @@ -2162,13 +2162,63 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner | ... | @@ -2162,13 +2162,63 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2162 | return noreturn_src_node; | 2162 | return noreturn_src_node; |
| 2163 | } | 2163 | } |
| 2164 | 2164 | ||
| 2165 | fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct { | ||
| 2166 | have_any: bool, | ||
| 2167 | have_normal: bool, | ||
| 2168 | have_err: bool, | ||
| 2169 | need_err_code: bool, | ||
| 2170 | } { | ||
| 2171 | const tree = astgen.tree; | ||
| 2172 | const node_datas = tree.nodes.items(.data); | ||
| 2173 | |||
| 2174 | var have_normal = false; | ||
| 2175 | var have_err = false; | ||
| 2176 | var need_err_code = false; | ||
| 2177 | var scope = inner_scope; | ||
| 2178 | while (scope != outer_scope) { | ||
| 2179 | switch (scope.tag) { | ||
| 2180 | .gen_zir => scope = scope.cast(GenZir).?.parent, | ||
| 2181 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | ||
| 2182 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | ||
| 2183 | .defer_normal => { | ||
| 2184 | const defer_scope = scope.cast(Scope.Defer).?; | ||
| 2185 | scope = defer_scope.parent; | ||
| 2186 | |||
| 2187 | have_normal = true; | ||
| 2188 | }, | ||
| 2189 | .defer_error => { | ||
| 2190 | const defer_scope = scope.cast(Scope.Defer).?; | ||
| 2191 | scope = defer_scope.parent; | ||
| 2192 | |||
| 2193 | have_err = true; | ||
| 2194 | |||
| 2195 | const have_err_payload = node_datas[defer_scope.defer_node].lhs != 0; | ||
| 2196 | need_err_code = need_err_code or have_err_payload; | ||
| 2197 | }, | ||
| 2198 | .namespace => unreachable, | ||
| 2199 | .top => unreachable, | ||
| 2200 | } | ||
| 2201 | } | ||
| 2202 | return .{ | ||
| 2203 | .have_any = have_normal or have_err, | ||
| 2204 | .have_normal = have_normal, | ||
| 2205 | .have_err = have_err, | ||
| 2206 | .need_err_code = need_err_code, | ||
| 2207 | }; | ||
| 2208 | } | ||
| 2209 | |||
| 2210 | const DefersToEmit = union(enum) { | ||
| 2211 | both: Zir.Inst.Ref, // err code | ||
| 2212 | both_sans_err, | ||
| 2213 | normal_only, | ||
| 2214 | }; | ||
| 2215 | |||
| 2165 | fn genDefers( | 2216 | fn genDefers( |
| 2166 | gz: *GenZir, | 2217 | gz: *GenZir, |
| 2167 | outer_scope: *Scope, | 2218 | outer_scope: *Scope, |
| 2168 | inner_scope: *Scope, | 2219 | inner_scope: *Scope, |
| 2169 | err_code: Zir.Inst.Ref, | 2220 | which_ones: DefersToEmit, |
| 2170 | ) InnerError!void { | 2221 | ) InnerError!void { |
| 2171 | _ = err_code; | ||
| 2172 | const astgen = gz.astgen; | 2222 | const astgen = gz.astgen; |
| 2173 | const tree = astgen.tree; | 2223 | const tree = astgen.tree; |
| 2174 | const node_datas = tree.nodes.items(.data); | 2224 | const node_datas = tree.nodes.items(.data); |
| ... | @@ -2191,12 +2241,37 @@ fn genDefers( | ... | @@ -2191,12 +2241,37 @@ fn genDefers( |
| 2191 | .defer_error => { | 2241 | .defer_error => { |
| 2192 | const defer_scope = scope.cast(Scope.Defer).?; | 2242 | const defer_scope = scope.cast(Scope.Defer).?; |
| 2193 | scope = defer_scope.parent; | 2243 | scope = defer_scope.parent; |
| 2194 | if (err_code == .none) continue; | 2244 | switch (which_ones) { |
| 2195 | const expr_node = node_datas[defer_scope.defer_node].rhs; | 2245 | .both_sans_err => { |
| 2196 | const prev_in_defer = gz.in_defer; | 2246 | const expr_node = node_datas[defer_scope.defer_node].rhs; |
| 2197 | gz.in_defer = true; | 2247 | const prev_in_defer = gz.in_defer; |
| 2198 | defer gz.in_defer = prev_in_defer; | 2248 | gz.in_defer = true; |
| 2199 | _ = try unusedResultExpr(gz, defer_scope.parent, expr_node); | 2249 | defer gz.in_defer = prev_in_defer; |
| 2250 | _ = try unusedResultExpr(gz, defer_scope.parent, expr_node); | ||
| 2251 | }, | ||
| 2252 | .both => |err_code| { | ||
| 2253 | const expr_node = node_datas[defer_scope.defer_node].rhs; | ||
| 2254 | const payload_token = node_datas[defer_scope.defer_node].lhs; | ||
| 2255 | const prev_in_defer = gz.in_defer; | ||
| 2256 | gz.in_defer = true; | ||
| 2257 | defer gz.in_defer = prev_in_defer; | ||
| 2258 | var local_val_scope: Scope.LocalVal = undefined; | ||
| 2259 | const sub_scope = if (payload_token == 0) defer_scope.parent else blk: { | ||
| 2260 | const ident_name = try astgen.identAsString(payload_token); | ||
| 2261 | local_val_scope = .{ | ||
| 2262 | .parent = defer_scope.parent, | ||
| 2263 | .gen_zir = gz, | ||
| 2264 | .name = ident_name, | ||
| 2265 | .inst = err_code, | ||
| 2266 | .token_src = payload_token, | ||
| 2267 | .id_cat = .@"capture", | ||
| 2268 | }; | ||
| 2269 | break :blk &local_val_scope.base; | ||
| 2270 | }; | ||
| 2271 | _ = try unusedResultExpr(gz, sub_scope, expr_node); | ||
| 2272 | }, | ||
| 2273 | .normal_only => continue, | ||
| 2274 | } | ||
| 2200 | }, | 2275 | }, |
| 2201 | .namespace => unreachable, | 2276 | .namespace => unreachable, |
| 2202 | .top => unreachable, | 2277 | .top => unreachable, |
| ... | @@ -4544,8 +4619,8 @@ fn tryExpr( | ... | @@ -4544,8 +4619,8 @@ fn tryExpr( |
| 4544 | }; | 4619 | }; |
| 4545 | const err_ops = switch (rl) { | 4620 | const err_ops = switch (rl) { |
| 4546 | // zig fmt: off | 4621 | // zig fmt: off |
| 4547 | .ref => [3]Zir.Inst.Tag{ .is_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr }, | 4622 | .ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr }, |
| 4548 | else => [3]Zir.Inst.Tag{ .is_err, .err_union_code, .err_union_payload_unsafe }, | 4623 | else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe }, |
| 4549 | // zig fmt: on | 4624 | // zig fmt: on |
| 4550 | }; | 4625 | }; |
| 4551 | // This could be a pointer or value depending on the `operand_rl` parameter. | 4626 | // This could be a pointer or value depending on the `operand_rl` parameter. |
| ... | @@ -4563,21 +4638,21 @@ fn tryExpr( | ... | @@ -4563,21 +4638,21 @@ fn tryExpr( |
| 4563 | var then_scope = parent_gz.makeSubBlock(scope); | 4638 | var then_scope = parent_gz.makeSubBlock(scope); |
| 4564 | defer then_scope.instructions.deinit(astgen.gpa); | 4639 | defer then_scope.instructions.deinit(astgen.gpa); |
| 4565 | 4640 | ||
| 4566 | const err_code = try then_scope.addUnNode(err_ops[1], operand, node); | ||
| 4567 | try genDefers(&then_scope, &fn_block.base, scope, err_code); | ||
| 4568 | const then_result = try then_scope.addUnNode(.ret_node, err_code, node); | ||
| 4569 | |||
| 4570 | var else_scope = parent_gz.makeSubBlock(scope); | ||
| 4571 | defer else_scope.instructions.deinit(astgen.gpa); | ||
| 4572 | |||
| 4573 | block_scope.break_count += 1; | 4641 | block_scope.break_count += 1; |
| 4574 | // This could be a pointer or value depending on `err_ops[2]`. | 4642 | // This could be a pointer or value depending on `err_ops[2]`. |
| 4575 | const unwrapped_payload = try else_scope.addUnNode(err_ops[2], operand, node); | 4643 | const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node); |
| 4576 | const else_result = switch (rl) { | 4644 | const then_result = switch (rl) { |
| 4577 | .ref => unwrapped_payload, | 4645 | .ref => unwrapped_payload, |
| 4578 | else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node), | 4646 | else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node), |
| 4579 | }; | 4647 | }; |
| 4580 | 4648 | ||
| 4649 | var else_scope = parent_gz.makeSubBlock(scope); | ||
| 4650 | defer else_scope.instructions.deinit(astgen.gpa); | ||
| 4651 | |||
| 4652 | const err_code = try else_scope.addUnNode(err_ops[1], operand, node); | ||
| 4653 | try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); | ||
| 4654 | const else_result = try else_scope.addUnNode(.ret_node, err_code, node); | ||
| 4655 | |||
| 4581 | return finishThenElseBlock( | 4656 | return finishThenElseBlock( |
| 4582 | parent_gz, | 4657 | parent_gz, |
| 4583 | rl, | 4658 | rl, |
| ... | @@ -4634,18 +4709,28 @@ fn orelseCatchExpr( | ... | @@ -4634,18 +4709,28 @@ fn orelseCatchExpr( |
| 4634 | var then_scope = parent_gz.makeSubBlock(scope); | 4709 | var then_scope = parent_gz.makeSubBlock(scope); |
| 4635 | defer then_scope.instructions.deinit(astgen.gpa); | 4710 | defer then_scope.instructions.deinit(astgen.gpa); |
| 4636 | 4711 | ||
| 4712 | // This could be a pointer or value depending on `unwrap_op`. | ||
| 4713 | const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node); | ||
| 4714 | const then_result = switch (rl) { | ||
| 4715 | .ref => unwrapped_payload, | ||
| 4716 | else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node), | ||
| 4717 | }; | ||
| 4718 | |||
| 4719 | var else_scope = parent_gz.makeSubBlock(scope); | ||
| 4720 | defer else_scope.instructions.deinit(astgen.gpa); | ||
| 4721 | |||
| 4637 | var err_val_scope: Scope.LocalVal = undefined; | 4722 | var err_val_scope: Scope.LocalVal = undefined; |
| 4638 | const then_sub_scope = blk: { | 4723 | const else_sub_scope = blk: { |
| 4639 | const payload = payload_token orelse break :blk &then_scope.base; | 4724 | const payload = payload_token orelse break :blk &else_scope.base; |
| 4640 | if (mem.eql(u8, tree.tokenSlice(payload), "_")) { | 4725 | if (mem.eql(u8, tree.tokenSlice(payload), "_")) { |
| 4641 | return astgen.failTok(payload, "discard of error capture; omit it instead", .{}); | 4726 | return astgen.failTok(payload, "discard of error capture; omit it instead", .{}); |
| 4642 | } | 4727 | } |
| 4643 | const err_name = try astgen.identAsString(payload); | 4728 | const err_name = try astgen.identAsString(payload); |
| 4644 | err_val_scope = .{ | 4729 | err_val_scope = .{ |
| 4645 | .parent = &then_scope.base, | 4730 | .parent = &else_scope.base, |
| 4646 | .gen_zir = &then_scope, | 4731 | .gen_zir = &else_scope, |
| 4647 | .name = err_name, | 4732 | .name = err_name, |
| 4648 | .inst = try then_scope.addUnNode(unwrap_code_op, operand, node), | 4733 | .inst = try else_scope.addUnNode(unwrap_code_op, operand, node), |
| 4649 | .token_src = payload, | 4734 | .token_src = payload, |
| 4650 | .id_cat = .@"capture", | 4735 | .id_cat = .@"capture", |
| 4651 | }; | 4736 | }; |
| ... | @@ -4653,23 +4738,13 @@ fn orelseCatchExpr( | ... | @@ -4653,23 +4738,13 @@ fn orelseCatchExpr( |
| 4653 | }; | 4738 | }; |
| 4654 | 4739 | ||
| 4655 | block_scope.break_count += 1; | 4740 | block_scope.break_count += 1; |
| 4656 | const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, rhs); | 4741 | const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs); |
| 4657 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); | 4742 | try checkUsed(parent_gz, &else_scope.base, else_sub_scope); |
| 4658 | 4743 | ||
| 4659 | // We hold off on the break instructions as well as copying the then/else | 4744 | // We hold off on the break instructions as well as copying the then/else |
| 4660 | // instructions into place until we know whether to keep store_to_block_ptr | 4745 | // instructions into place until we know whether to keep store_to_block_ptr |
| 4661 | // instructions or not. | 4746 | // instructions or not. |
| 4662 | 4747 | ||
| 4663 | var else_scope = parent_gz.makeSubBlock(scope); | ||
| 4664 | defer else_scope.instructions.deinit(astgen.gpa); | ||
| 4665 | |||
| 4666 | // This could be a pointer or value depending on `unwrap_op`. | ||
| 4667 | const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node); | ||
| 4668 | const else_result = switch (rl) { | ||
| 4669 | .ref => unwrapped_payload, | ||
| 4670 | else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node), | ||
| 4671 | }; | ||
| 4672 | |||
| 4673 | return finishThenElseBlock( | 4748 | return finishThenElseBlock( |
| 4674 | parent_gz, | 4749 | parent_gz, |
| 4675 | rl, | 4750 | rl, |
| ... | @@ -4887,7 +4962,7 @@ fn ifExpr( | ... | @@ -4887,7 +4962,7 @@ fn ifExpr( |
| 4887 | if (if_full.error_token) |_| { | 4962 | if (if_full.error_token) |_| { |
| 4888 | const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none; | 4963 | const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none; |
| 4889 | const err_union = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr); | 4964 | const err_union = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr); |
| 4890 | const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err; | 4965 | const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; |
| 4891 | break :c .{ | 4966 | break :c .{ |
| 4892 | .inst = err_union, | 4967 | .inst = err_union, |
| 4893 | .bool_bit = try block_scope.addUnNode(tag, err_union, node), | 4968 | .bool_bit = try block_scope.addUnNode(tag, err_union, node), |
| ... | @@ -5144,7 +5219,7 @@ fn whileExpr( | ... | @@ -5144,7 +5219,7 @@ fn whileExpr( |
| 5144 | if (while_full.error_token) |_| { | 5219 | if (while_full.error_token) |_| { |
| 5145 | const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none; | 5220 | const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none; |
| 5146 | const err_union = try expr(&continue_scope, &continue_scope.base, cond_rl, while_full.ast.cond_expr); | 5221 | const err_union = try expr(&continue_scope, &continue_scope.base, cond_rl, while_full.ast.cond_expr); |
| 5147 | const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err; | 5222 | const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; |
| 5148 | break :c .{ | 5223 | break :c .{ |
| 5149 | .inst = err_union, | 5224 | .inst = err_union, |
| 5150 | .bool_bit = try continue_scope.addUnNode(tag, err_union, node), | 5225 | .bool_bit = try continue_scope.addUnNode(tag, err_union, node), |
| ... | @@ -6090,17 +6165,37 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6090,17 +6165,37 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6090 | const astgen = gz.astgen; | 6165 | const astgen = gz.astgen; |
| 6091 | const tree = astgen.tree; | 6166 | const tree = astgen.tree; |
| 6092 | const node_datas = tree.nodes.items(.data); | 6167 | const node_datas = tree.nodes.items(.data); |
| 6168 | const node_tags = tree.nodes.items(.tag); | ||
| 6093 | 6169 | ||
| 6094 | if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{}); | 6170 | if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{}); |
| 6095 | 6171 | ||
| 6172 | const defer_outer = &astgen.fn_block.?.base; | ||
| 6173 | |||
| 6096 | const operand_node = node_datas[node].lhs; | 6174 | const operand_node = node_datas[node].lhs; |
| 6097 | if (operand_node == 0) { | 6175 | if (operand_node == 0) { |
| 6098 | // Returning a void value; skip error defers. | 6176 | // Returning a void value; skip error defers. |
| 6099 | try genDefers(gz, &astgen.fn_block.?.base, scope, .none); | 6177 | try genDefers(gz, defer_outer, scope, .normal_only); |
| 6100 | _ = try gz.addUnNode(.ret_node, .void_value, node); | 6178 | _ = try gz.addUnNode(.ret_node, .void_value, node); |
| 6101 | return Zir.Inst.Ref.unreachable_value; | 6179 | return Zir.Inst.Ref.unreachable_value; |
| 6102 | } | 6180 | } |
| 6103 | 6181 | ||
| 6182 | if (node_tags[operand_node] == .error_value) { | ||
| 6183 | // Hot path for `return error.Foo`. This bypasses result location logic as well as logic | ||
| 6184 | // for detecting whether to add something to the function's inferred error set. | ||
| 6185 | const ident_token = node_datas[operand_node].rhs; | ||
| 6186 | const err_name_str_index = try astgen.identAsString(ident_token); | ||
| 6187 | const defer_counts = countDefers(astgen, defer_outer, scope); | ||
| 6188 | if (!defer_counts.need_err_code) { | ||
| 6189 | try genDefers(gz, defer_outer, scope, .both_sans_err); | ||
| 6190 | _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token); | ||
| 6191 | return Zir.Inst.Ref.unreachable_value; | ||
| 6192 | } | ||
| 6193 | const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token); | ||
| 6194 | try genDefers(gz, defer_outer, scope, .{ .both = err_code }); | ||
| 6195 | _ = try gz.addUnNode(.ret_node, err_code, node); | ||
| 6196 | return Zir.Inst.Ref.unreachable_value; | ||
| 6197 | } | ||
| 6198 | |||
| 6104 | const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{ | 6199 | const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{ |
| 6105 | .ptr = try gz.addNodeExtended(.ret_ptr, node), | 6200 | .ptr = try gz.addNodeExtended(.ret_ptr, node), |
| 6106 | } else .{ | 6201 | } else .{ |
| ... | @@ -6111,34 +6206,46 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6111,34 +6206,46 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6111 | switch (nodeMayEvalToError(tree, operand_node)) { | 6206 | switch (nodeMayEvalToError(tree, operand_node)) { |
| 6112 | .never => { | 6207 | .never => { |
| 6113 | // Returning a value that cannot be an error; skip error defers. | 6208 | // Returning a value that cannot be an error; skip error defers. |
| 6114 | try genDefers(gz, &astgen.fn_block.?.base, scope, .none); | 6209 | try genDefers(gz, defer_outer, scope, .normal_only); |
| 6115 | _ = try gz.addUnNode(.ret_node, operand, node); | 6210 | _ = try gz.addUnNode(.ret_node, operand, node); |
| 6116 | return Zir.Inst.Ref.unreachable_value; | 6211 | return Zir.Inst.Ref.unreachable_value; |
| 6117 | }, | 6212 | }, |
| 6118 | .always => { | 6213 | .always => { |
| 6119 | // Value is always an error. Emit both error defers and regular defers. | 6214 | // Value is always an error. Emit both error defers and regular defers. |
| 6120 | const err_code = try gz.addUnNode(.err_union_code, operand, node); | 6215 | const err_code = try gz.addUnNode(.err_union_code, operand, node); |
| 6121 | try genDefers(gz, &astgen.fn_block.?.base, scope, err_code); | 6216 | try genDefers(gz, defer_outer, scope, .{ .both = err_code }); |
| 6122 | _ = try gz.addUnNode(.ret_node, operand, node); | 6217 | _ = try gz.addUnNode(.ret_node, operand, node); |
| 6123 | return Zir.Inst.Ref.unreachable_value; | 6218 | return Zir.Inst.Ref.unreachable_value; |
| 6124 | }, | 6219 | }, |
| 6125 | .maybe => { | 6220 | .maybe => { |
| 6221 | const defer_counts = countDefers(astgen, defer_outer, scope); | ||
| 6222 | if (!defer_counts.have_err) { | ||
| 6223 | // Only regular defers; no branch needed. | ||
| 6224 | try genDefers(gz, defer_outer, scope, .normal_only); | ||
| 6225 | _ = try gz.addUnNode(.ret_node, operand, node); | ||
| 6226 | return Zir.Inst.Ref.unreachable_value; | ||
| 6227 | } | ||
| 6228 | |||
| 6126 | // Emit conditional branch for generating errdefers. | 6229 | // Emit conditional branch for generating errdefers. |
| 6127 | const is_err = try gz.addUnNode(.is_err, operand, node); | 6230 | const is_non_err = try gz.addUnNode(.is_non_err, operand, node); |
| 6128 | const condbr = try gz.addCondBr(.condbr, node); | 6231 | const condbr = try gz.addCondBr(.condbr, node); |
| 6129 | 6232 | ||
| 6130 | var then_scope = gz.makeSubBlock(scope); | 6233 | var then_scope = gz.makeSubBlock(scope); |
| 6131 | defer then_scope.instructions.deinit(astgen.gpa); | 6234 | defer then_scope.instructions.deinit(astgen.gpa); |
| 6132 | const err_code = try then_scope.addUnNode(.err_union_code, operand, node); | 6235 | |
| 6133 | try genDefers(&then_scope, &astgen.fn_block.?.base, scope, err_code); | 6236 | try genDefers(&then_scope, defer_outer, scope, .normal_only); |
| 6134 | _ = try then_scope.addUnNode(.ret_node, operand, node); | 6237 | _ = try then_scope.addUnNode(.ret_node, operand, node); |
| 6135 | 6238 | ||
| 6136 | var else_scope = gz.makeSubBlock(scope); | 6239 | var else_scope = gz.makeSubBlock(scope); |
| 6137 | defer else_scope.instructions.deinit(astgen.gpa); | 6240 | defer else_scope.instructions.deinit(astgen.gpa); |
| 6138 | try genDefers(&else_scope, &astgen.fn_block.?.base, scope, .none); | 6241 | |
| 6242 | const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{ | ||
| 6243 | .both = try else_scope.addUnNode(.err_union_code, operand, node), | ||
| 6244 | }; | ||
| 6245 | try genDefers(&else_scope, defer_outer, scope, which_ones); | ||
| 6139 | _ = try else_scope.addUnNode(.ret_node, operand, node); | 6246 | _ = try else_scope.addUnNode(.ret_node, operand, node); |
| 6140 | 6247 | ||
| 6141 | try setCondBrPayload(condbr, is_err, &then_scope, &else_scope); | 6248 | try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope); |
| 6142 | 6249 | ||
| 6143 | return Zir.Inst.Ref.unreachable_value; | 6250 | return Zir.Inst.Ref.unreachable_value; |
| 6144 | }, | 6251 | }, |
| ... | @@ -6885,7 +6992,7 @@ fn builtinCall( | ... | @@ -6885,7 +6992,7 @@ fn builtinCall( |
| 6885 | .field => { | 6992 | .field => { |
| 6886 | const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); | 6993 | const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); |
| 6887 | if (rl == .ref) { | 6994 | if (rl == .ref) { |
| 6888 | return try gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ | 6995 | return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ |
| 6889 | .lhs = try expr(gz, scope, .ref, params[0]), | 6996 | .lhs = try expr(gz, scope, .ref, params[0]), |
| 6890 | .field_name = field_name, | 6997 | .field_name = field_name, |
| 6891 | }); | 6998 | }); |
src/Module.zig+17-2| ... | @@ -755,6 +755,7 @@ pub const Fn = struct { | ... | @@ -755,6 +755,7 @@ pub const Fn = struct { |
| 755 | rbrace_column: u16, | 755 | rbrace_column: u16, |
| 756 | 756 | ||
| 757 | state: Analysis, | 757 | state: Analysis, |
| 758 | is_cold: bool = false, | ||
| 758 | 759 | ||
| 759 | pub const Analysis = enum { | 760 | pub const Analysis = enum { |
| 760 | queued, | 761 | queued, |
| ... | @@ -776,8 +777,19 @@ pub const Fn = struct { | ... | @@ -776,8 +777,19 @@ pub const Fn = struct { |
| 776 | } | 777 | } |
| 777 | 778 | ||
| 778 | pub fn deinit(func: *Fn, gpa: *Allocator) void { | 779 | pub fn deinit(func: *Fn, gpa: *Allocator) void { |
| 779 | _ = func; | 780 | if (func.getInferredErrorSet()) |map| { |
| 780 | _ = gpa; | 781 | map.deinit(gpa); |
| 782 | } | ||
| 783 | } | ||
| 784 | |||
| 785 | pub fn getInferredErrorSet(func: *Fn) ?*std.StringHashMapUnmanaged(void) { | ||
| 786 | const ret_ty = func.owner_decl.ty.fnReturnType(); | ||
| 787 | if (ret_ty.zigTypeTag() == .ErrorUnion) { | ||
| 788 | if (ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| { | ||
| 789 | return &payload.data.map; | ||
| 790 | } | ||
| 791 | } | ||
| 792 | return null; | ||
| 781 | } | 793 | } |
| 782 | }; | 794 | }; |
| 783 | 795 | ||
| ... | @@ -3453,6 +3465,9 @@ pub fn clearDecl( | ... | @@ -3453,6 +3465,9 @@ pub fn clearDecl( |
| 3453 | for (decl.dependencies.keys()) |dep| { | 3465 | for (decl.dependencies.keys()) |dep| { |
| 3454 | dep.removeDependant(decl); | 3466 | dep.removeDependant(decl); |
| 3455 | if (dep.dependants.count() == 0 and !dep.deletion_flag) { | 3467 | if (dep.dependants.count() == 0 and !dep.deletion_flag) { |
| 3468 | log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{ | ||
| 3469 | decl, decl.name, dep, dep.name, | ||
| 3470 | }); | ||
| 3456 | // We don't recursively perform a deletion here, because during the update, | 3471 | // We don't recursively perform a deletion here, because during the update, |
| 3457 | // another reference to it may turn up. | 3472 | // another reference to it may turn up. |
| 3458 | dep.deletion_flag = true; | 3473 | dep.deletion_flag = true; |
src/Sema.zig+224-88| ... | @@ -225,12 +225,10 @@ pub fn analyzeBody( | ... | @@ -225,12 +225,10 @@ pub fn analyzeBody( |
| 225 | .float => try sema.zirFloat(block, inst), | 225 | .float => try sema.zirFloat(block, inst), |
| 226 | .float128 => try sema.zirFloat128(block, inst), | 226 | .float128 => try sema.zirFloat128(block, inst), |
| 227 | .int_type => try sema.zirIntType(block, inst), | 227 | .int_type => try sema.zirIntType(block, inst), |
| 228 | .is_err => try sema.zirIsErr(block, inst), | 228 | .is_non_err => try sema.zirIsNonErr(block, inst), |
| 229 | .is_err_ptr => try sema.zirIsErrPtr(block, inst), | 229 | .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), |
| 230 | .is_non_null => try sema.zirIsNull(block, inst, true), | 230 | .is_non_null => try sema.zirIsNonNull(block, inst), |
| 231 | .is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true), | 231 | .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), |
| 232 | .is_null => try sema.zirIsNull(block, inst, false), | ||
| 233 | .is_null_ptr => try sema.zirIsNullPtr(block, inst, false), | ||
| 234 | .loop => try sema.zirLoop(block, inst), | 232 | .loop => try sema.zirLoop(block, inst), |
| 235 | .merge_error_sets => try sema.zirMergeErrorSets(block, inst), | 233 | .merge_error_sets => try sema.zirMergeErrorSets(block, inst), |
| 236 | .negate => try sema.zirNegate(block, inst, .sub), | 234 | .negate => try sema.zirNegate(block, inst, .sub), |
| ... | @@ -244,6 +242,7 @@ pub fn analyzeBody( | ... | @@ -244,6 +242,7 @@ pub fn analyzeBody( |
| 244 | .ptr_type => try sema.zirPtrType(block, inst), | 242 | .ptr_type => try sema.zirPtrType(block, inst), |
| 245 | .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst), | 243 | .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst), |
| 246 | .ref => try sema.zirRef(block, inst), | 244 | .ref => try sema.zirRef(block, inst), |
| 245 | .ret_err_value_code => try sema.zirRetErrValueCode(block, inst), | ||
| 247 | .shl => try sema.zirShl(block, inst), | 246 | .shl => try sema.zirShl(block, inst), |
| 248 | .shr => try sema.zirShr(block, inst), | 247 | .shr => try sema.zirShr(block, inst), |
| 249 | .slice_end => try sema.zirSliceEnd(block, inst), | 248 | .slice_end => try sema.zirSliceEnd(block, inst), |
| ... | @@ -380,8 +379,9 @@ pub fn analyzeBody( | ... | @@ -380,8 +379,9 @@ pub fn analyzeBody( |
| 380 | .condbr => return sema.zirCondbr(block, inst), | 379 | .condbr => return sema.zirCondbr(block, inst), |
| 381 | .@"break" => return sema.zirBreak(block, inst), | 380 | .@"break" => return sema.zirBreak(block, inst), |
| 382 | .compile_error => return sema.zirCompileError(block, inst), | 381 | .compile_error => return sema.zirCompileError(block, inst), |
| 383 | .ret_coerce => return sema.zirRetTok(block, inst, true), | 382 | .ret_coerce => return sema.zirRetCoerce(block, inst, true), |
| 384 | .ret_node => return sema.zirRetNode(block, inst), | 383 | .ret_node => return sema.zirRetNode(block, inst), |
| 384 | .ret_err_value => return sema.zirRetErrValue(block, inst), | ||
| 385 | .@"unreachable" => return sema.zirUnreachable(block, inst), | 385 | .@"unreachable" => return sema.zirUnreachable(block, inst), |
| 386 | .repeat => return sema.zirRepeat(block, inst), | 386 | .repeat => return sema.zirRepeat(block, inst), |
| 387 | .panic => return sema.zirPanic(block, inst), | 387 | .panic => return sema.zirPanic(block, inst), |
| ... | @@ -587,6 +587,19 @@ pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) error{OutOfMemory}!*ir.In | ... | @@ -587,6 +587,19 @@ pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) error{OutOfMemory}!*ir.In |
| 587 | return sema.inst_map.get(@intCast(u32, i)).?; | 587 | return sema.inst_map.get(@intCast(u32, i)).?; |
| 588 | } | 588 | } |
| 589 | 589 | ||
| 590 | fn resolveConstBool( | ||
| 591 | sema: *Sema, | ||
| 592 | block: *Scope.Block, | ||
| 593 | src: LazySrcLoc, | ||
| 594 | zir_ref: Zir.Inst.Ref, | ||
| 595 | ) !bool { | ||
| 596 | const air_inst = try sema.resolveInst(zir_ref); | ||
| 597 | const wanted_type = Type.initTag(.bool); | ||
| 598 | const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); | ||
| 599 | const val = try sema.resolveConstValue(block, src, coerced_inst); | ||
| 600 | return val.toBool(); | ||
| 601 | } | ||
| 602 | |||
| 590 | fn resolveConstString( | 603 | fn resolveConstString( |
| 591 | sema: *Sema, | 604 | sema: *Sema, |
| 592 | block: *Scope.Block, | 605 | block: *Scope.Block, |
| ... | @@ -1754,8 +1767,9 @@ fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError! | ... | @@ -1754,8 +1767,9 @@ fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError! |
| 1754 | fn zirPanic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index { | 1767 | fn zirPanic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index { |
| 1755 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 1768 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 1756 | const src: LazySrcLoc = inst_data.src(); | 1769 | const src: LazySrcLoc = inst_data.src(); |
| 1757 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirPanic", .{}); | 1770 | const msg_inst = try sema.resolveInst(inst_data.operand); |
| 1758 | //return always_noreturn; | 1771 | |
| 1772 | return sema.panicWithMsg(block, src, msg_inst); | ||
| 1759 | } | 1773 | } |
| 1760 | 1774 | ||
| 1761 | fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { | 1775 | fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { |
| ... | @@ -2028,8 +2042,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne | ... | @@ -2028,8 +2042,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne |
| 2028 | 2042 | ||
| 2029 | fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { | 2043 | fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| 2030 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 2044 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 2031 | const src: LazySrcLoc = inst_data.src(); | 2045 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 2032 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetCold", .{}); | 2046 | const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand); |
| 2047 | const func = sema.func orelse return; // does nothing outside a function | ||
| 2048 | func.is_cold = is_cold; | ||
| 2033 | } | 2049 | } |
| 2034 | 2050 | ||
| 2035 | fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { | 2051 | fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| ... | @@ -2041,11 +2057,7 @@ fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner | ... | @@ -2041,11 +2057,7 @@ fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner |
| 2041 | fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { | 2057 | fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| 2042 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 2058 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 2043 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 2059 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 2044 | 2060 | block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand); | |
| 2045 | const op = try sema.resolveInst(inst_data.operand); | ||
| 2046 | const op_coerced = try sema.coerce(block, Type.initTag(.bool), op, operand_src); | ||
| 2047 | const b = (try sema.resolveConstValue(block, operand_src, op_coerced)).toBool(); | ||
| 2048 | block.want_safety = b; | ||
| 2049 | } | 2061 | } |
| 2050 | 2062 | ||
| 2051 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { | 2063 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| ... | @@ -2190,21 +2202,27 @@ fn zirCall( | ... | @@ -2190,21 +2202,27 @@ fn zirCall( |
| 2190 | const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index); | 2202 | const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index); |
| 2191 | const args = sema.code.refSlice(extra.end, extra.data.args_len); | 2203 | const args = sema.code.refSlice(extra.end, extra.data.args_len); |
| 2192 | 2204 | ||
| 2193 | return sema.analyzeCall(block, extra.data.callee, func_src, call_src, modifier, ensure_result_used, args); | 2205 | const func = try sema.resolveInst(extra.data.callee); |
| 2206 | // TODO handle function calls of generic functions | ||
| 2207 | const resolved_args = try sema.arena.alloc(*Inst, args.len); | ||
| 2208 | for (args) |zir_arg, i| { | ||
| 2209 | // the args are already casted to the result of a param type instruction. | ||
| 2210 | resolved_args[i] = try sema.resolveInst(zir_arg); | ||
| 2211 | } | ||
| 2212 | |||
| 2213 | return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args); | ||
| 2194 | } | 2214 | } |
| 2195 | 2215 | ||
| 2196 | fn analyzeCall( | 2216 | fn analyzeCall( |
| 2197 | sema: *Sema, | 2217 | sema: *Sema, |
| 2198 | block: *Scope.Block, | 2218 | block: *Scope.Block, |
| 2199 | zir_func: Zir.Inst.Ref, | 2219 | func: *ir.Inst, |
| 2200 | func_src: LazySrcLoc, | 2220 | func_src: LazySrcLoc, |
| 2201 | call_src: LazySrcLoc, | 2221 | call_src: LazySrcLoc, |
| 2202 | modifier: std.builtin.CallOptions.Modifier, | 2222 | modifier: std.builtin.CallOptions.Modifier, |
| 2203 | ensure_result_used: bool, | 2223 | ensure_result_used: bool, |
| 2204 | zir_args: []const Zir.Inst.Ref, | 2224 | args: []const *ir.Inst, |
| 2205 | ) InnerError!*ir.Inst { | 2225 | ) InnerError!*ir.Inst { |
| 2206 | const func = try sema.resolveInst(zir_func); | ||
| 2207 | |||
| 2208 | if (func.ty.zigTypeTag() != .Fn) | 2226 | if (func.ty.zigTypeTag() != .Fn) |
| 2209 | return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty}); | 2227 | return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty}); |
| 2210 | 2228 | ||
| ... | @@ -2221,22 +2239,22 @@ fn analyzeCall( | ... | @@ -2221,22 +2239,22 @@ fn analyzeCall( |
| 2221 | const fn_params_len = func.ty.fnParamLen(); | 2239 | const fn_params_len = func.ty.fnParamLen(); |
| 2222 | if (func.ty.fnIsVarArgs()) { | 2240 | if (func.ty.fnIsVarArgs()) { |
| 2223 | assert(cc == .C); | 2241 | assert(cc == .C); |
| 2224 | if (zir_args.len < fn_params_len) { | 2242 | if (args.len < fn_params_len) { |
| 2225 | // TODO add error note: declared here | 2243 | // TODO add error note: declared here |
| 2226 | return sema.mod.fail( | 2244 | return sema.mod.fail( |
| 2227 | &block.base, | 2245 | &block.base, |
| 2228 | func_src, | 2246 | func_src, |
| 2229 | "expected at least {d} argument(s), found {d}", | 2247 | "expected at least {d} argument(s), found {d}", |
| 2230 | .{ fn_params_len, zir_args.len }, | 2248 | .{ fn_params_len, args.len }, |
| 2231 | ); | 2249 | ); |
| 2232 | } | 2250 | } |
| 2233 | } else if (fn_params_len != zir_args.len) { | 2251 | } else if (fn_params_len != args.len) { |
| 2234 | // TODO add error note: declared here | 2252 | // TODO add error note: declared here |
| 2235 | return sema.mod.fail( | 2253 | return sema.mod.fail( |
| 2236 | &block.base, | 2254 | &block.base, |
| 2237 | func_src, | 2255 | func_src, |
| 2238 | "expected {d} argument(s), found {d}", | 2256 | "expected {d} argument(s), found {d}", |
| 2239 | .{ fn_params_len, zir_args.len }, | 2257 | .{ fn_params_len, args.len }, |
| 2240 | ); | 2258 | ); |
| 2241 | } | 2259 | } |
| 2242 | 2260 | ||
| ... | @@ -2256,13 +2274,6 @@ fn analyzeCall( | ... | @@ -2256,13 +2274,6 @@ fn analyzeCall( |
| 2256 | }), | 2274 | }), |
| 2257 | } | 2275 | } |
| 2258 | 2276 | ||
| 2259 | // TODO handle function calls of generic functions | ||
| 2260 | const casted_args = try sema.arena.alloc(*Inst, zir_args.len); | ||
| 2261 | for (zir_args) |zir_arg, i| { | ||
| 2262 | // the args are already casted to the result of a param type instruction. | ||
| 2263 | casted_args[i] = try sema.resolveInst(zir_arg); | ||
| 2264 | } | ||
| 2265 | |||
| 2266 | const ret_type = func.ty.fnReturnType(); | 2277 | const ret_type = func.ty.fnReturnType(); |
| 2267 | 2278 | ||
| 2268 | const is_comptime_call = block.is_comptime or modifier == .compile_time; | 2279 | const is_comptime_call = block.is_comptime or modifier == .compile_time; |
| ... | @@ -2323,7 +2334,7 @@ fn analyzeCall( | ... | @@ -2323,7 +2334,7 @@ fn analyzeCall( |
| 2323 | defer sema.func = parent_func; | 2334 | defer sema.func = parent_func; |
| 2324 | 2335 | ||
| 2325 | const parent_param_inst_list = sema.param_inst_list; | 2336 | const parent_param_inst_list = sema.param_inst_list; |
| 2326 | sema.param_inst_list = casted_args; | 2337 | sema.param_inst_list = args; |
| 2327 | defer sema.param_inst_list = parent_param_inst_list; | 2338 | defer sema.param_inst_list = parent_param_inst_list; |
| 2328 | 2339 | ||
| 2329 | const parent_next_arg_index = sema.next_arg_index; | 2340 | const parent_next_arg_index = sema.next_arg_index; |
| ... | @@ -2357,7 +2368,7 @@ fn analyzeCall( | ... | @@ -2357,7 +2368,7 @@ fn analyzeCall( |
| 2357 | break :res result; | 2368 | break :res result; |
| 2358 | } else res: { | 2369 | } else res: { |
| 2359 | try sema.requireRuntimeBlock(block, call_src); | 2370 | try sema.requireRuntimeBlock(block, call_src); |
| 2360 | break :res try block.addCall(call_src, ret_type, func, casted_args); | 2371 | break :res try block.addCall(call_src, ret_type, func, args); |
| 2361 | }; | 2372 | }; |
| 2362 | 2373 | ||
| 2363 | if (ensure_result_used) { | 2374 | if (ensure_result_used) { |
| ... | @@ -2968,17 +2979,19 @@ fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner | ... | @@ -2968,17 +2979,19 @@ fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner |
| 2968 | if (operand.ty.zigTypeTag() != .ErrorUnion) | 2979 | if (operand.ty.zigTypeTag() != .ErrorUnion) |
| 2969 | return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty}); | 2980 | return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty}); |
| 2970 | 2981 | ||
| 2982 | const result_ty = operand.ty.castTag(.error_union).?.data.error_set; | ||
| 2983 | |||
| 2971 | if (operand.value()) |val| { | 2984 | if (operand.value()) |val| { |
| 2972 | assert(val.getError() != null); | 2985 | assert(val.getError() != null); |
| 2973 | const data = val.castTag(.error_union).?.data; | 2986 | const data = val.castTag(.error_union).?.data; |
| 2974 | return sema.mod.constInst(sema.arena, src, .{ | 2987 | return sema.mod.constInst(sema.arena, src, .{ |
| 2975 | .ty = operand.ty.castTag(.error_union).?.data.error_set, | 2988 | .ty = result_ty, |
| 2976 | .val = data, | 2989 | .val = data, |
| 2977 | }); | 2990 | }); |
| 2978 | } | 2991 | } |
| 2979 | 2992 | ||
| 2980 | try sema.requireRuntimeBlock(block, src); | 2993 | try sema.requireRuntimeBlock(block, src); |
| 2981 | return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand); | 2994 | return block.addUnOp(src, result_ty, .unwrap_errunion_err, operand); |
| 2982 | } | 2995 | } |
| 2983 | 2996 | ||
| 2984 | /// Pointer in, value out | 2997 | /// Pointer in, value out |
| ... | @@ -2994,18 +3007,20 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In | ... | @@ -2994,18 +3007,20 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In |
| 2994 | if (operand.ty.elemType().zigTypeTag() != .ErrorUnion) | 3007 | if (operand.ty.elemType().zigTypeTag() != .ErrorUnion) |
| 2995 | return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()}); | 3008 | return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()}); |
| 2996 | 3009 | ||
| 3010 | const result_ty = operand.ty.elemType().castTag(.error_union).?.data.error_set; | ||
| 3011 | |||
| 2997 | if (operand.value()) |pointer_val| { | 3012 | if (operand.value()) |pointer_val| { |
| 2998 | const val = try pointer_val.pointerDeref(sema.arena); | 3013 | const val = try pointer_val.pointerDeref(sema.arena); |
| 2999 | assert(val.getError() != null); | 3014 | assert(val.getError() != null); |
| 3000 | const data = val.castTag(.error_union).?.data; | 3015 | const data = val.castTag(.error_union).?.data; |
| 3001 | return sema.mod.constInst(sema.arena, src, .{ | 3016 | return sema.mod.constInst(sema.arena, src, .{ |
| 3002 | .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set, | 3017 | .ty = result_ty, |
| 3003 | .val = data, | 3018 | .val = data, |
| 3004 | }); | 3019 | }); |
| 3005 | } | 3020 | } |
| 3006 | 3021 | ||
| 3007 | try sema.requireRuntimeBlock(block, src); | 3022 | try sema.requireRuntimeBlock(block, src); |
| 3008 | return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand); | 3023 | return block.addUnOp(src, result_ty, .unwrap_errunion_err_ptr, operand); |
| 3009 | } | 3024 | } |
| 3010 | 3025 | ||
| 3011 | fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { | 3026 | fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| ... | @@ -3081,28 +3096,31 @@ fn funcCommon( | ... | @@ -3081,28 +3096,31 @@ fn funcCommon( |
| 3081 | ) InnerError!*Inst { | 3096 | ) InnerError!*Inst { |
| 3082 | const src: LazySrcLoc = .{ .node_offset = src_node_offset }; | 3097 | const src: LazySrcLoc = .{ .node_offset = src_node_offset }; |
| 3083 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; | 3098 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; |
| 3084 | const return_type = try sema.resolveType(block, ret_ty_src, zir_return_type); | 3099 | const bare_return_type = try sema.resolveType(block, ret_ty_src, zir_return_type); |
| 3085 | 3100 | ||
| 3086 | const mod = sema.mod; | 3101 | const mod = sema.mod; |
| 3087 | 3102 | ||
| 3103 | const new_func = if (body_inst == 0) undefined else try sema.gpa.create(Module.Fn); | ||
| 3104 | errdefer if (body_inst != 0) sema.gpa.destroy(new_func); | ||
| 3105 | |||
| 3088 | const fn_ty: Type = fn_ty: { | 3106 | const fn_ty: Type = fn_ty: { |
| 3089 | // Hot path for some common function types. | 3107 | // Hot path for some common function types. |
| 3090 | if (zir_param_types.len == 0 and !var_args and align_val.tag() == .null_value and | 3108 | if (zir_param_types.len == 0 and !var_args and align_val.tag() == .null_value and |
| 3091 | !inferred_error_set) | 3109 | !inferred_error_set) |
| 3092 | { | 3110 | { |
| 3093 | if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { | 3111 | if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { |
| 3094 | break :fn_ty Type.initTag(.fn_noreturn_no_args); | 3112 | break :fn_ty Type.initTag(.fn_noreturn_no_args); |
| 3095 | } | 3113 | } |
| 3096 | 3114 | ||
| 3097 | if (return_type.zigTypeTag() == .Void and cc == .Unspecified) { | 3115 | if (bare_return_type.zigTypeTag() == .Void and cc == .Unspecified) { |
| 3098 | break :fn_ty Type.initTag(.fn_void_no_args); | 3116 | break :fn_ty Type.initTag(.fn_void_no_args); |
| 3099 | } | 3117 | } |
| 3100 | 3118 | ||
| 3101 | if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) { | 3119 | if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Naked) { |
| 3102 | break :fn_ty Type.initTag(.fn_naked_noreturn_no_args); | 3120 | break :fn_ty Type.initTag(.fn_naked_noreturn_no_args); |
| 3103 | } | 3121 | } |
| 3104 | 3122 | ||
| 3105 | if (return_type.zigTypeTag() == .Void and cc == .C) { | 3123 | if (bare_return_type.zigTypeTag() == .Void and cc == .C) { |
| 3106 | break :fn_ty Type.initTag(.fn_ccc_void_no_args); | 3124 | break :fn_ty Type.initTag(.fn_ccc_void_no_args); |
| 3107 | } | 3125 | } |
| 3108 | } | 3126 | } |
| ... | @@ -3120,9 +3138,16 @@ fn funcCommon( | ... | @@ -3120,9 +3138,16 @@ fn funcCommon( |
| 3120 | return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{}); | 3138 | return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{}); |
| 3121 | } | 3139 | } |
| 3122 | 3140 | ||
| 3123 | if (inferred_error_set) { | 3141 | const return_type = if (!inferred_error_set) bare_return_type else blk: { |
| 3124 | return mod.fail(&block.base, src, "TODO implement functions with inferred error sets", .{}); | 3142 | const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, .{ |
| 3125 | } | 3143 | .func = new_func, |
| 3144 | .map = .{}, | ||
| 3145 | }); | ||
| 3146 | break :blk try Type.Tag.error_union.create(sema.arena, .{ | ||
| 3147 | .error_set = error_set_ty, | ||
| 3148 | .payload = bare_return_type, | ||
| 3149 | }); | ||
| 3150 | }; | ||
| 3126 | 3151 | ||
| 3127 | break :fn_ty try Type.Tag.function.create(sema.arena, .{ | 3152 | break :fn_ty try Type.Tag.function.create(sema.arena, .{ |
| 3128 | .param_types = param_types, | 3153 | .param_types = param_types, |
| ... | @@ -3188,7 +3213,6 @@ fn funcCommon( | ... | @@ -3188,7 +3213,6 @@ fn funcCommon( |
| 3188 | const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued; | 3213 | const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued; |
| 3189 | 3214 | ||
| 3190 | const fn_payload = try sema.arena.create(Value.Payload.Function); | 3215 | const fn_payload = try sema.arena.create(Value.Payload.Function); |
| 3191 | const new_func = try sema.gpa.create(Module.Fn); | ||
| 3192 | new_func.* = .{ | 3216 | new_func.* = .{ |
| 3193 | .state = anal_state, | 3217 | .state = anal_state, |
| 3194 | .zir_body_inst = body_inst, | 3218 | .zir_body_inst = body_inst, |
| ... | @@ -4542,6 +4566,12 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError! | ... | @@ -4542,6 +4566,12 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError! |
| 4542 | return mod.constType(sema.arena, src, file_root_decl.ty); | 4566 | return mod.constType(sema.arena, src, file_root_decl.ty); |
| 4543 | } | 4567 | } |
| 4544 | 4568 | ||
| 4569 | fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { | ||
| 4570 | _ = block; | ||
| 4571 | _ = inst; | ||
| 4572 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{}); | ||
| 4573 | } | ||
| 4574 | |||
| 4545 | fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { | 4575 | fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { |
| 4546 | const tracy = trace(@src()); | 4576 | const tracy = trace(@src()); |
| 4547 | defer tracy.end(); | 4577 | defer tracy.end(); |
| ... | @@ -5273,11 +5303,10 @@ fn zirBoolBr( | ... | @@ -5273,11 +5303,10 @@ fn zirBoolBr( |
| 5273 | return &block_inst.base; | 5303 | return &block_inst.base; |
| 5274 | } | 5304 | } |
| 5275 | 5305 | ||
| 5276 | fn zirIsNull( | 5306 | fn zirIsNonNull( |
| 5277 | sema: *Sema, | 5307 | sema: *Sema, |
| 5278 | block: *Scope.Block, | 5308 | block: *Scope.Block, |
| 5279 | inst: Zir.Inst.Index, | 5309 | inst: Zir.Inst.Index, |
| 5280 | invert_logic: bool, | ||
| 5281 | ) InnerError!*Inst { | 5310 | ) InnerError!*Inst { |
| 5282 | const tracy = trace(@src()); | 5311 | const tracy = trace(@src()); |
| 5283 | defer tracy.end(); | 5312 | defer tracy.end(); |
| ... | @@ -5285,14 +5314,13 @@ fn zirIsNull( | ... | @@ -5285,14 +5314,13 @@ fn zirIsNull( |
| 5285 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 5314 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 5286 | const src = inst_data.src(); | 5315 | const src = inst_data.src(); |
| 5287 | const operand = try sema.resolveInst(inst_data.operand); | 5316 | const operand = try sema.resolveInst(inst_data.operand); |
| 5288 | return sema.analyzeIsNull(block, src, operand, invert_logic); | 5317 | return sema.analyzeIsNull(block, src, operand, true); |
| 5289 | } | 5318 | } |
| 5290 | 5319 | ||
| 5291 | fn zirIsNullPtr( | 5320 | fn zirIsNonNullPtr( |
| 5292 | sema: *Sema, | 5321 | sema: *Sema, |
| 5293 | block: *Scope.Block, | 5322 | block: *Scope.Block, |
| 5294 | inst: Zir.Inst.Index, | 5323 | inst: Zir.Inst.Index, |
| 5295 | invert_logic: bool, | ||
| 5296 | ) InnerError!*Inst { | 5324 | ) InnerError!*Inst { |
| 5297 | const tracy = trace(@src()); | 5325 | const tracy = trace(@src()); |
| 5298 | defer tracy.end(); | 5326 | defer tracy.end(); |
| ... | @@ -5301,19 +5329,19 @@ fn zirIsNullPtr( | ... | @@ -5301,19 +5329,19 @@ fn zirIsNullPtr( |
| 5301 | const src = inst_data.src(); | 5329 | const src = inst_data.src(); |
| 5302 | const ptr = try sema.resolveInst(inst_data.operand); | 5330 | const ptr = try sema.resolveInst(inst_data.operand); |
| 5303 | const loaded = try sema.analyzeLoad(block, src, ptr, src); | 5331 | const loaded = try sema.analyzeLoad(block, src, ptr, src); |
| 5304 | return sema.analyzeIsNull(block, src, loaded, invert_logic); | 5332 | return sema.analyzeIsNull(block, src, loaded, true); |
| 5305 | } | 5333 | } |
| 5306 | 5334 | ||
| 5307 | fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { | 5335 | fn zirIsNonErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { |
| 5308 | const tracy = trace(@src()); | 5336 | const tracy = trace(@src()); |
| 5309 | defer tracy.end(); | 5337 | defer tracy.end(); |
| 5310 | 5338 | ||
| 5311 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 5339 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 5312 | const operand = try sema.resolveInst(inst_data.operand); | 5340 | const operand = try sema.resolveInst(inst_data.operand); |
| 5313 | return sema.analyzeIsErr(block, inst_data.src(), operand); | 5341 | return sema.analyzeIsNonErr(block, inst_data.src(), operand); |
| 5314 | } | 5342 | } |
| 5315 | 5343 | ||
| 5316 | fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { | 5344 | fn zirIsNonErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { |
| 5317 | const tracy = trace(@src()); | 5345 | const tracy = trace(@src()); |
| 5318 | defer tracy.end(); | 5346 | defer tracy.end(); |
| 5319 | 5347 | ||
| ... | @@ -5321,7 +5349,7 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro | ... | @@ -5321,7 +5349,7 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro |
| 5321 | const src = inst_data.src(); | 5349 | const src = inst_data.src(); |
| 5322 | const ptr = try sema.resolveInst(inst_data.operand); | 5350 | const ptr = try sema.resolveInst(inst_data.operand); |
| 5323 | const loaded = try sema.analyzeLoad(block, src, ptr, src); | 5351 | const loaded = try sema.analyzeLoad(block, src, ptr, src); |
| 5324 | return sema.analyzeIsErr(block, src, loaded); | 5352 | return sema.analyzeIsNonErr(block, src, loaded); |
| 5325 | } | 5353 | } |
| 5326 | 5354 | ||
| 5327 | fn zirCondbr( | 5355 | fn zirCondbr( |
| ... | @@ -5388,7 +5416,31 @@ fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE | ... | @@ -5388,7 +5416,31 @@ fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE |
| 5388 | } | 5416 | } |
| 5389 | } | 5417 | } |
| 5390 | 5418 | ||
| 5391 | fn zirRetTok( | 5419 | fn zirRetErrValue( |
| 5420 | sema: *Sema, | ||
| 5421 | block: *Scope.Block, | ||
| 5422 | inst: Zir.Inst.Index, | ||
| 5423 | ) InnerError!Zir.Inst.Index { | ||
| 5424 | const inst_data = sema.code.instructions.items(.data)[inst].str_tok; | ||
| 5425 | const err_name = inst_data.get(sema.code); | ||
| 5426 | const src = inst_data.src(); | ||
| 5427 | |||
| 5428 | // Add the error tag to the inferred error set of the in-scope function. | ||
| 5429 | if (sema.func) |func| { | ||
| 5430 | if (func.getInferredErrorSet()) |map| { | ||
| 5431 | _ = try map.getOrPut(sema.gpa, err_name); | ||
| 5432 | } | ||
| 5433 | } | ||
| 5434 | // Return the error code from the function. | ||
| 5435 | const kv = try sema.mod.getErrorValue(err_name); | ||
| 5436 | const result_inst = try sema.mod.constInst(sema.arena, src, .{ | ||
| 5437 | .ty = try Type.Tag.error_set_single.create(sema.arena, kv.key), | ||
| 5438 | .val = try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }), | ||
| 5439 | }); | ||
| 5440 | return sema.analyzeRet(block, result_inst, src, true); | ||
| 5441 | } | ||
| 5442 | |||
| 5443 | fn zirRetCoerce( | ||
| 5392 | sema: *Sema, | 5444 | sema: *Sema, |
| 5393 | block: *Scope.Block, | 5445 | block: *Scope.Block, |
| 5394 | inst: Zir.Inst.Index, | 5446 | inst: Zir.Inst.Index, |
| ... | @@ -6195,6 +6247,10 @@ fn zirFuncExtended( | ... | @@ -6195,6 +6247,10 @@ fn zirFuncExtended( |
| 6195 | src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; | 6247 | src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; |
| 6196 | } | 6248 | } |
| 6197 | 6249 | ||
| 6250 | const is_var_args = small.is_var_args; | ||
| 6251 | const is_inferred_error = small.is_inferred_error; | ||
| 6252 | const is_extern = small.is_extern; | ||
| 6253 | |||
| 6198 | return sema.funcCommon( | 6254 | return sema.funcCommon( |
| 6199 | block, | 6255 | block, |
| 6200 | extra.data.src_node, | 6256 | extra.data.src_node, |
| ... | @@ -6203,9 +6259,9 @@ fn zirFuncExtended( | ... | @@ -6203,9 +6259,9 @@ fn zirFuncExtended( |
| 6203 | extra.data.return_type, | 6259 | extra.data.return_type, |
| 6204 | cc, | 6260 | cc, |
| 6205 | align_val, | 6261 | align_val, |
| 6206 | small.is_var_args, | 6262 | is_var_args, |
| 6207 | small.is_inferred_error, | 6263 | is_inferred_error, |
| 6208 | small.is_extern, | 6264 | is_extern, |
| 6209 | src_locs, | 6265 | src_locs, |
| 6210 | lib_name, | 6266 | lib_name, |
| 6211 | ); | 6267 | ); |
| ... | @@ -6357,15 +6413,75 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: | ... | @@ -6357,15 +6413,75 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: |
| 6357 | try parent_block.instructions.append(sema.gpa, &block_inst.base); | 6413 | try parent_block.instructions.append(sema.gpa, &block_inst.base); |
| 6358 | } | 6414 | } |
| 6359 | 6415 | ||
| 6360 | fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !Zir.Inst.Index { | 6416 | fn panicWithMsg( |
| 6361 | _ = sema; | 6417 | sema: *Sema, |
| 6362 | _ = panic_id; | 6418 | block: *Scope.Block, |
| 6363 | // TODO Once we have a panic function to call, call it here instead of breakpoint. | 6419 | src: LazySrcLoc, |
| 6364 | _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint); | 6420 | msg_inst: *ir.Inst, |
| 6365 | _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach); | 6421 | ) !Zir.Inst.Index { |
| 6422 | const mod = sema.mod; | ||
| 6423 | const arena = sema.arena; | ||
| 6424 | |||
| 6425 | const this_feature_is_implemented_in_the_backend = | ||
| 6426 | mod.comp.bin_file.options.object_format == .c; | ||
| 6427 | if (!this_feature_is_implemented_in_the_backend) { | ||
| 6428 | // TODO implement this feature in all the backends and then delete this branch | ||
| 6429 | _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint); | ||
| 6430 | _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach); | ||
| 6431 | return always_noreturn; | ||
| 6432 | } | ||
| 6433 | const panic_fn = try sema.getBuiltin(block, src, "panic"); | ||
| 6434 | const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); | ||
| 6435 | const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); | ||
| 6436 | const ptr_stack_trace_ty = try mod.simplePtrType(arena, stack_trace_ty, true, .One); | ||
| 6437 | const null_stack_trace = try mod.constInst(arena, src, .{ | ||
| 6438 | .ty = try mod.optionalType(arena, ptr_stack_trace_ty), | ||
| 6439 | .val = Value.initTag(.null_value), | ||
| 6440 | }); | ||
| 6441 | const args = try arena.create([2]*ir.Inst); | ||
| 6442 | args.* = .{ msg_inst, null_stack_trace }; | ||
| 6443 | _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args); | ||
| 6366 | return always_noreturn; | 6444 | return always_noreturn; |
| 6367 | } | 6445 | } |
| 6368 | 6446 | ||
| 6447 | fn safetyPanic( | ||
| 6448 | sema: *Sema, | ||
| 6449 | block: *Scope.Block, | ||
| 6450 | src: LazySrcLoc, | ||
| 6451 | panic_id: PanicId, | ||
| 6452 | ) !Zir.Inst.Index { | ||
| 6453 | const msg = switch (panic_id) { | ||
| 6454 | .unreach => "reached unreachable code", | ||
| 6455 | .unwrap_null => "attempt to use null value", | ||
| 6456 | .unwrap_errunion => "unreachable error occurred", | ||
| 6457 | .cast_to_null => "cast causes pointer to be null", | ||
| 6458 | .incorrect_alignment => "incorrect alignment", | ||
| 6459 | .invalid_error_code => "invalid error code", | ||
| 6460 | }; | ||
| 6461 | |||
| 6462 | const msg_inst = msg_inst: { | ||
| 6463 | // TODO instead of making a new decl for every panic in the entire compilation, | ||
| 6464 | // introduce the concept of a reference-counted decl for these | ||
| 6465 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | ||
| 6466 | errdefer new_decl_arena.deinit(); | ||
| 6467 | |||
| 6468 | const decl_ty = try Type.Tag.array_u8.create(&new_decl_arena.allocator, msg.len); | ||
| 6469 | const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, msg); | ||
| 6470 | |||
| 6471 | const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{ | ||
| 6472 | .ty = decl_ty, | ||
| 6473 | .val = decl_val, | ||
| 6474 | }); | ||
| 6475 | errdefer sema.mod.deleteAnonDecl(&block.base, new_decl); | ||
| 6476 | try new_decl.finalizeNewArena(&new_decl_arena); | ||
| 6477 | break :msg_inst try sema.analyzeDeclRef(block, .unneeded, new_decl); | ||
| 6478 | }; | ||
| 6479 | |||
| 6480 | const casted_msg_inst = try sema.coerce(block, Type.initTag(.const_slice_u8), msg_inst, src); | ||
| 6481 | |||
| 6482 | return sema.panicWithMsg(block, src, casted_msg_inst); | ||
| 6483 | } | ||
| 6484 | |||
| 6369 | fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { | 6485 | fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { |
| 6370 | sema.branch_count += 1; | 6486 | sema.branch_count += 1; |
| 6371 | if (sema.branch_count > sema.branch_quota) { | 6487 | if (sema.branch_count > sema.branch_quota) { |
| ... | @@ -7102,20 +7218,25 @@ fn analyzeIsNull( | ... | @@ -7102,20 +7218,25 @@ fn analyzeIsNull( |
| 7102 | return block.addUnOp(src, result_ty, inst_tag, operand); | 7218 | return block.addUnOp(src, result_ty, inst_tag, operand); |
| 7103 | } | 7219 | } |
| 7104 | 7220 | ||
| 7105 | fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst { | 7221 | fn analyzeIsNonErr( |
| 7222 | sema: *Sema, | ||
| 7223 | block: *Scope.Block, | ||
| 7224 | src: LazySrcLoc, | ||
| 7225 | operand: *Inst, | ||
| 7226 | ) InnerError!*Inst { | ||
| 7106 | const ot = operand.ty.zigTypeTag(); | 7227 | const ot = operand.ty.zigTypeTag(); |
| 7107 | if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false); | 7228 | if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, true); |
| 7108 | if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true); | 7229 | if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, false); |
| 7109 | assert(ot == .ErrorUnion); | 7230 | assert(ot == .ErrorUnion); |
| 7110 | const result_ty = Type.initTag(.bool); | 7231 | const result_ty = Type.initTag(.bool); |
| 7111 | if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| { | 7232 | if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| { |
| 7112 | if (err_union.isUndef()) { | 7233 | if (err_union.isUndef()) { |
| 7113 | return sema.mod.constUndef(sema.arena, src, result_ty); | 7234 | return sema.mod.constUndef(sema.arena, src, result_ty); |
| 7114 | } | 7235 | } |
| 7115 | return sema.mod.constBool(sema.arena, src, err_union.getError() != null); | 7236 | return sema.mod.constBool(sema.arena, src, err_union.getError() == null); |
| 7116 | } | 7237 | } |
| 7117 | try sema.requireRuntimeBlock(block, src); | 7238 | try sema.requireRuntimeBlock(block, src); |
| 7118 | return block.addUnOp(src, result_ty, .is_err, operand); | 7239 | return block.addUnOp(src, result_ty, .is_non_err, operand); |
| 7119 | } | 7240 | } |
| 7120 | 7241 | ||
| 7121 | fn analyzeSlice( | 7242 | fn analyzeSlice( |
| ... | @@ -7377,15 +7498,13 @@ fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) | ... | @@ -7377,15 +7498,13 @@ fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) |
| 7377 | } | 7498 | } |
| 7378 | 7499 | ||
| 7379 | fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | 7500 | fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { |
| 7380 | // TODO deal with inferred error sets | ||
| 7381 | const err_union = dest_type.castTag(.error_union).?; | 7501 | const err_union = dest_type.castTag(.error_union).?; |
| 7382 | if (inst.value()) |val| { | 7502 | if (inst.value()) |val| { |
| 7383 | const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: { | 7503 | if (inst.ty.zigTypeTag() != .ErrorSet) { |
| 7384 | _ = try sema.coerce(block, err_union.data.payload, inst, inst.src); | 7504 | _ = try sema.coerce(block, err_union.data.payload, inst, inst.src); |
| 7385 | break :blk val; | ||
| 7386 | } else switch (err_union.data.error_set.tag()) { | 7505 | } else switch (err_union.data.error_set.tag()) { |
| 7387 | .anyerror => val, | 7506 | .anyerror => {}, |
| 7388 | .error_set_single => blk: { | 7507 | .error_set_single => { |
| 7389 | const expected_name = val.castTag(.@"error").?.data.name; | 7508 | const expected_name = val.castTag(.@"error").?.data.name; |
| 7390 | const n = err_union.data.error_set.castTag(.error_set_single).?.data; | 7509 | const n = err_union.data.error_set.castTag(.error_set_single).?.data; |
| 7391 | if (!mem.eql(u8, expected_name, n)) { | 7510 | if (!mem.eql(u8, expected_name, n)) { |
| ... | @@ -7396,9 +7515,8 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst | ... | @@ -7396,9 +7515,8 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst |
| 7396 | .{ err_union.data.error_set, inst.ty }, | 7515 | .{ err_union.data.error_set, inst.ty }, |
| 7397 | ); | 7516 | ); |
| 7398 | } | 7517 | } |
| 7399 | break :blk val; | ||
| 7400 | }, | 7518 | }, |
| 7401 | .error_set => blk: { | 7519 | .error_set => { |
| 7402 | const expected_name = val.castTag(.@"error").?.data.name; | 7520 | const expected_name = val.castTag(.@"error").?.data.name; |
| 7403 | const error_set = err_union.data.error_set.castTag(.error_set).?.data; | 7521 | const error_set = err_union.data.error_set.castTag(.error_set).?.data; |
| 7404 | const names = error_set.names_ptr[0..error_set.names_len]; | 7522 | const names = error_set.names_ptr[0..error_set.names_len]; |
| ... | @@ -7415,18 +7533,26 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst | ... | @@ -7415,18 +7533,26 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst |
| 7415 | .{ err_union.data.error_set, inst.ty }, | 7533 | .{ err_union.data.error_set, inst.ty }, |
| 7416 | ); | 7534 | ); |
| 7417 | } | 7535 | } |
| 7418 | break :blk val; | 7536 | }, |
| 7537 | .error_set_inferred => { | ||
| 7538 | const expected_name = val.castTag(.@"error").?.data.name; | ||
| 7539 | const map = &err_union.data.error_set.castTag(.error_set_inferred).?.data.map; | ||
| 7540 | if (!map.contains(expected_name)) { | ||
| 7541 | return sema.mod.fail( | ||
| 7542 | &block.base, | ||
| 7543 | inst.src, | ||
| 7544 | "expected type '{}', found type '{}'", | ||
| 7545 | .{ err_union.data.error_set, inst.ty }, | ||
| 7546 | ); | ||
| 7547 | } | ||
| 7419 | }, | 7548 | }, |
| 7420 | else => unreachable, | 7549 | else => unreachable, |
| 7421 | }; | 7550 | } |
| 7422 | 7551 | ||
| 7423 | return sema.mod.constInst(sema.arena, inst.src, .{ | 7552 | return sema.mod.constInst(sema.arena, inst.src, .{ |
| 7424 | .ty = dest_type, | 7553 | .ty = dest_type, |
| 7425 | // creating a SubValue for the error_union payload | 7554 | // creating a SubValue for the error_union payload |
| 7426 | .val = try Value.Tag.error_union.create( | 7555 | .val = try Value.Tag.error_union.create(sema.arena, val), |
| 7427 | sema.arena, | ||
| 7428 | to_wrap, | ||
| 7429 | ), | ||
| 7430 | }); | 7556 | }); |
| 7431 | } | 7557 | } |
| 7432 | 7558 | ||
| ... | @@ -7573,12 +7699,12 @@ fn resolveBuiltinTypeFields( | ... | @@ -7573,12 +7699,12 @@ fn resolveBuiltinTypeFields( |
| 7573 | return sema.resolveTypeFields(block, src, resolved_ty); | 7699 | return sema.resolveTypeFields(block, src, resolved_ty); |
| 7574 | } | 7700 | } |
| 7575 | 7701 | ||
| 7576 | fn getBuiltinType( | 7702 | fn getBuiltin( |
| 7577 | sema: *Sema, | 7703 | sema: *Sema, |
| 7578 | block: *Scope.Block, | 7704 | block: *Scope.Block, |
| 7579 | src: LazySrcLoc, | 7705 | src: LazySrcLoc, |
| 7580 | name: []const u8, | 7706 | name: []const u8, |
| 7581 | ) InnerError!Type { | 7707 | ) InnerError!*ir.Inst { |
| 7582 | const mod = sema.mod; | 7708 | const mod = sema.mod; |
| 7583 | const std_pkg = mod.root_pkg.table.get("std").?; | 7709 | const std_pkg = mod.root_pkg.table.get("std").?; |
| 7584 | const std_file = (mod.importPkg(std_pkg) catch unreachable).file; | 7710 | const std_file = (mod.importPkg(std_pkg) catch unreachable).file; |
| ... | @@ -7596,7 +7722,16 @@ fn getBuiltinType( | ... | @@ -7596,7 +7722,16 @@ fn getBuiltinType( |
| 7596 | builtin_ty.getNamespace().?, | 7722 | builtin_ty.getNamespace().?, |
| 7597 | name, | 7723 | name, |
| 7598 | ); | 7724 | ); |
| 7599 | const ty_inst = try sema.analyzeLoad(block, src, opt_ty_inst.?, src); | 7725 | return sema.analyzeLoad(block, src, opt_ty_inst.?, src); |
| 7726 | } | ||
| 7727 | |||
| 7728 | fn getBuiltinType( | ||
| 7729 | sema: *Sema, | ||
| 7730 | block: *Scope.Block, | ||
| 7731 | src: LazySrcLoc, | ||
| 7732 | name: []const u8, | ||
| 7733 | ) InnerError!Type { | ||
| 7734 | const ty_inst = try sema.getBuiltin(block, src, name); | ||
| 7600 | return sema.resolveAirAsType(block, src, ty_inst); | 7735 | return sema.resolveAirAsType(block, src, ty_inst); |
| 7601 | } | 7736 | } |
| 7602 | 7737 | ||
| ... | @@ -7662,6 +7797,7 @@ fn typeHasOnePossibleValue( | ... | @@ -7662,6 +7797,7 @@ fn typeHasOnePossibleValue( |
| 7662 | .error_union, | 7797 | .error_union, |
| 7663 | .error_set, | 7798 | .error_set, |
| 7664 | .error_set_single, | 7799 | .error_set_single, |
| 7800 | .error_set_inferred, | ||
| 7665 | .@"opaque", | 7801 | .@"opaque", |
| 7666 | .var_args_param, | 7802 | .var_args_param, |
| 7667 | .manyptr_u8, | 7803 | .manyptr_u8, |
src/Zir.zig+33-26| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | //! Zig Intermediate Representation. Astgen.zig converts AST nodes to these | 1 | //! Zig Intermediate Representation. Astgen.zig converts AST nodes to these |
| 2 | //! untyped IR instructions. Next, Sema.zig processes these into TZIR. | 2 | //! untyped IR instructions. Next, Sema.zig processes these into AIR. |
| 3 | //! The minimum amount of information needed to represent a list of ZIR instructions. | 3 | //! The minimum amount of information needed to represent a list of ZIR instructions. |
| 4 | //! Once this structure is completed, it can be used to generate TZIR, followed by | 4 | //! Once this structure is completed, it can be used to generate AIR, followed by |
| 5 | //! machine code, without any memory access into the AST tree token list, node list, | 5 | //! machine code, without any memory access into the AST tree token list, node list, |
| 6 | //! or source bytes. Exceptions include: | 6 | //! or source bytes. Exceptions include: |
| 7 | //! * Compile errors, which may need to reach into these data structures to | 7 | //! * Compile errors, which may need to reach into these data structures to |
| ... | @@ -398,26 +398,20 @@ pub const Inst = struct { | ... | @@ -398,26 +398,20 @@ pub const Inst = struct { |
| 398 | /// Return a boolean false if an optional is null. `x != null` | 398 | /// Return a boolean false if an optional is null. `x != null` |
| 399 | /// Uses the `un_node` field. | 399 | /// Uses the `un_node` field. |
| 400 | is_non_null, | 400 | is_non_null, |
| 401 | /// Return a boolean true if an optional is null. `x == null` | ||
| 402 | /// Uses the `un_node` field. | ||
| 403 | is_null, | ||
| 404 | /// Return a boolean false if an optional is null. `x.* != null` | 401 | /// Return a boolean false if an optional is null. `x.* != null` |
| 405 | /// Uses the `un_node` field. | 402 | /// Uses the `un_node` field. |
| 406 | is_non_null_ptr, | 403 | is_non_null_ptr, |
| 407 | /// Return a boolean true if an optional is null. `x.* == null` | 404 | /// Return a boolean false if value is an error |
| 408 | /// Uses the `un_node` field. | ||
| 409 | is_null_ptr, | ||
| 410 | /// Return a boolean true if value is an error | ||
| 411 | /// Uses the `un_node` field. | 405 | /// Uses the `un_node` field. |
| 412 | is_err, | 406 | is_non_err, |
| 413 | /// Return a boolean true if dereferenced pointer is an error | 407 | /// Return a boolean false if dereferenced pointer is an error |
| 414 | /// Uses the `un_node` field. | 408 | /// Uses the `un_node` field. |
| 415 | is_err_ptr, | 409 | is_non_err_ptr, |
| 416 | /// A labeled block of code that loops forever. At the end of the body will have either | 410 | /// A labeled block of code that loops forever. At the end of the body will have either |
| 417 | /// a `repeat` instruction or a `repeat_inline` instruction. | 411 | /// a `repeat` instruction or a `repeat_inline` instruction. |
| 418 | /// Uses the `pl_node` field. The AST node is either a for loop or while loop. | 412 | /// Uses the `pl_node` field. The AST node is either a for loop or while loop. |
| 419 | /// This ZIR instruction is needed because TZIR does not (yet?) match ZIR, and Sema | 413 | /// This ZIR instruction is needed because AIR does not (yet?) match ZIR, and Sema |
| 420 | /// needs to emit more than 1 TZIR block for this instruction. | 414 | /// needs to emit more than 1 AIR block for this instruction. |
| 421 | /// The payload is `Block`. | 415 | /// The payload is `Block`. |
| 422 | loop, | 416 | loop, |
| 423 | /// Sends runtime control flow back to the beginning of the current block. | 417 | /// Sends runtime control flow back to the beginning of the current block. |
| ... | @@ -466,6 +460,19 @@ pub const Inst = struct { | ... | @@ -466,6 +460,19 @@ pub const Inst = struct { |
| 466 | /// Uses the `un_tok` union field. | 460 | /// Uses the `un_tok` union field. |
| 467 | /// The operand needs to get coerced to the function's return type. | 461 | /// The operand needs to get coerced to the function's return type. |
| 468 | ret_coerce, | 462 | ret_coerce, |
| 463 | /// Sends control flow back to the function's callee. | ||
| 464 | /// The return operand is `error.foo` where `foo` is given by the string. | ||
| 465 | /// If the current function has an inferred error set, the error given by the | ||
| 466 | /// name is added to it. | ||
| 467 | /// Uses the `str_tok` union field. | ||
| 468 | ret_err_value, | ||
| 469 | /// A string name is provided which is an anonymous error set value. | ||
| 470 | /// If the current function has an inferred error set, the error given by the | ||
| 471 | /// name is added to it. | ||
| 472 | /// Results in the error code. Note that control flow is not diverted with | ||
| 473 | /// this instruction; a following 'ret' instruction will do the diversion. | ||
| 474 | /// Uses the `str_tok` union field. | ||
| 475 | ret_err_value_code, | ||
| 469 | /// Create a pointer type that does not have a sentinel, alignment, or bit range specified. | 476 | /// Create a pointer type that does not have a sentinel, alignment, or bit range specified. |
| 470 | /// Uses the `ptr_type_simple` union field. | 477 | /// Uses the `ptr_type_simple` union field. |
| 471 | ptr_type_simple, | 478 | ptr_type_simple, |
| ... | @@ -1033,11 +1040,9 @@ pub const Inst = struct { | ... | @@ -1033,11 +1040,9 @@ pub const Inst = struct { |
| 1033 | .float128, | 1040 | .float128, |
| 1034 | .int_type, | 1041 | .int_type, |
| 1035 | .is_non_null, | 1042 | .is_non_null, |
| 1036 | .is_null, | ||
| 1037 | .is_non_null_ptr, | 1043 | .is_non_null_ptr, |
| 1038 | .is_null_ptr, | 1044 | .is_non_err, |
| 1039 | .is_err, | 1045 | .is_non_err_ptr, |
| 1040 | .is_err_ptr, | ||
| 1041 | .mod_rem, | 1046 | .mod_rem, |
| 1042 | .mul, | 1047 | .mul, |
| 1043 | .mulwrap, | 1048 | .mulwrap, |
| ... | @@ -1193,6 +1198,7 @@ pub const Inst = struct { | ... | @@ -1193,6 +1198,7 @@ pub const Inst = struct { |
| 1193 | .@"resume", | 1198 | .@"resume", |
| 1194 | .@"await", | 1199 | .@"await", |
| 1195 | .await_nosuspend, | 1200 | .await_nosuspend, |
| 1201 | .ret_err_value_code, | ||
| 1196 | .extended, | 1202 | .extended, |
| 1197 | => false, | 1203 | => false, |
| 1198 | 1204 | ||
| ... | @@ -1203,6 +1209,7 @@ pub const Inst = struct { | ... | @@ -1203,6 +1209,7 @@ pub const Inst = struct { |
| 1203 | .compile_error, | 1209 | .compile_error, |
| 1204 | .ret_node, | 1210 | .ret_node, |
| 1205 | .ret_coerce, | 1211 | .ret_coerce, |
| 1212 | .ret_err_value, | ||
| 1206 | .@"unreachable", | 1213 | .@"unreachable", |
| 1207 | .repeat, | 1214 | .repeat, |
| 1208 | .repeat_inline, | 1215 | .repeat_inline, |
| ... | @@ -1291,11 +1298,9 @@ pub const Inst = struct { | ... | @@ -1291,11 +1298,9 @@ pub const Inst = struct { |
| 1291 | .float128 = .pl_node, | 1298 | .float128 = .pl_node, |
| 1292 | .int_type = .int_type, | 1299 | .int_type = .int_type, |
| 1293 | .is_non_null = .un_node, | 1300 | .is_non_null = .un_node, |
| 1294 | .is_null = .un_node, | ||
| 1295 | .is_non_null_ptr = .un_node, | 1301 | .is_non_null_ptr = .un_node, |
| 1296 | .is_null_ptr = .un_node, | 1302 | .is_non_err = .un_node, |
| 1297 | .is_err = .un_node, | 1303 | .is_non_err_ptr = .un_node, |
| 1298 | .is_err_ptr = .un_node, | ||
| 1299 | .loop = .pl_node, | 1304 | .loop = .pl_node, |
| 1300 | .repeat = .node, | 1305 | .repeat = .node, |
| 1301 | .repeat_inline = .node, | 1306 | .repeat_inline = .node, |
| ... | @@ -1307,6 +1312,8 @@ pub const Inst = struct { | ... | @@ -1307,6 +1312,8 @@ pub const Inst = struct { |
| 1307 | .ref = .un_tok, | 1312 | .ref = .un_tok, |
| 1308 | .ret_node = .un_node, | 1313 | .ret_node = .un_node, |
| 1309 | .ret_coerce = .un_tok, | 1314 | .ret_coerce = .un_tok, |
| 1315 | .ret_err_value = .str_tok, | ||
| 1316 | .ret_err_value_code = .str_tok, | ||
| 1310 | .ptr_type_simple = .ptr_type_simple, | 1317 | .ptr_type_simple = .ptr_type_simple, |
| 1311 | .ptr_type = .ptr_type, | 1318 | .ptr_type = .ptr_type, |
| 1312 | .slice_start = .pl_node, | 1319 | .slice_start = .pl_node, |
| ... | @@ -2840,11 +2847,9 @@ const Writer = struct { | ... | @@ -2840,11 +2847,9 @@ const Writer = struct { |
| 2840 | .err_union_code, | 2847 | .err_union_code, |
| 2841 | .err_union_code_ptr, | 2848 | .err_union_code_ptr, |
| 2842 | .is_non_null, | 2849 | .is_non_null, |
| 2843 | .is_null, | ||
| 2844 | .is_non_null_ptr, | 2850 | .is_non_null_ptr, |
| 2845 | .is_null_ptr, | 2851 | .is_non_err, |
| 2846 | .is_err, | 2852 | .is_non_err_ptr, |
| 2847 | .is_err_ptr, | ||
| 2848 | .typeof, | 2853 | .typeof, |
| 2849 | .typeof_elem, | 2854 | .typeof_elem, |
| 2850 | .struct_init_empty, | 2855 | .struct_init_empty, |
| ... | @@ -3077,6 +3082,8 @@ const Writer = struct { | ... | @@ -3077,6 +3082,8 @@ const Writer = struct { |
| 3077 | .decl_val, | 3082 | .decl_val, |
| 3078 | .import, | 3083 | .import, |
| 3079 | .arg, | 3084 | .arg, |
| 3085 | .ret_err_value, | ||
| 3086 | .ret_err_value_code, | ||
| 3080 | => try self.writeStrTok(stream, inst), | 3087 | => try self.writeStrTok(stream, inst), |
| 3081 | 3088 | ||
| 3082 | .func => try self.writeFunc(stream, inst, false), | 3089 | .func => try self.writeFunc(stream, inst, false), |
src/air.zig+22-12| ... | @@ -90,8 +90,12 @@ pub const Inst = struct { | ... | @@ -90,8 +90,12 @@ pub const Inst = struct { |
| 90 | is_non_null_ptr, | 90 | is_non_null_ptr, |
| 91 | /// E!T => bool | 91 | /// E!T => bool |
| 92 | is_err, | 92 | is_err, |
| 93 | /// E!T => bool (inverted logic) | ||
| 94 | is_non_err, | ||
| 93 | /// *E!T => bool | 95 | /// *E!T => bool |
| 94 | is_err_ptr, | 96 | is_err_ptr, |
| 97 | /// *E!T => bool (inverted logic) | ||
| 98 | is_non_err_ptr, | ||
| 95 | bool_and, | 99 | bool_and, |
| 96 | bool_or, | 100 | bool_or, |
| 97 | /// Read a value from a pointer. | 101 | /// Read a value from a pointer. |
| ... | @@ -154,7 +158,9 @@ pub const Inst = struct { | ... | @@ -154,7 +158,9 @@ pub const Inst = struct { |
| 154 | .is_null, | 158 | .is_null, |
| 155 | .is_null_ptr, | 159 | .is_null_ptr, |
| 156 | .is_err, | 160 | .is_err, |
| 161 | .is_non_err, | ||
| 157 | .is_err_ptr, | 162 | .is_err_ptr, |
| 163 | .is_non_err_ptr, | ||
| 158 | .ptrtoint, | 164 | .ptrtoint, |
| 159 | .floatcast, | 165 | .floatcast, |
| 160 | .intcast, | 166 | .intcast, |
| ... | @@ -672,15 +678,15 @@ pub const Body = struct { | ... | @@ -672,15 +678,15 @@ pub const Body = struct { |
| 672 | /// For debugging purposes, prints a function representation to stderr. | 678 | /// For debugging purposes, prints a function representation to stderr. |
| 673 | pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { | 679 | pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { |
| 674 | const allocator = old_module.gpa; | 680 | const allocator = old_module.gpa; |
| 675 | var ctx: DumpTzir = .{ | 681 | var ctx: DumpAir = .{ |
| 676 | .allocator = allocator, | 682 | .allocator = allocator, |
| 677 | .arena = std.heap.ArenaAllocator.init(allocator), | 683 | .arena = std.heap.ArenaAllocator.init(allocator), |
| 678 | .old_module = &old_module, | 684 | .old_module = &old_module, |
| 679 | .module_fn = module_fn, | 685 | .module_fn = module_fn, |
| 680 | .indent = 2, | 686 | .indent = 2, |
| 681 | .inst_table = DumpTzir.InstTable.init(allocator), | 687 | .inst_table = DumpAir.InstTable.init(allocator), |
| 682 | .partial_inst_table = DumpTzir.InstTable.init(allocator), | 688 | .partial_inst_table = DumpAir.InstTable.init(allocator), |
| 683 | .const_table = DumpTzir.InstTable.init(allocator), | 689 | .const_table = DumpAir.InstTable.init(allocator), |
| 684 | }; | 690 | }; |
| 685 | defer ctx.inst_table.deinit(); | 691 | defer ctx.inst_table.deinit(); |
| 686 | defer ctx.partial_inst_table.deinit(); | 692 | defer ctx.partial_inst_table.deinit(); |
| ... | @@ -695,12 +701,12 @@ pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { | ... | @@ -695,12 +701,12 @@ pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { |
| 695 | .dependency_failure => std.debug.print("(dependency_failure)", .{}), | 701 | .dependency_failure => std.debug.print("(dependency_failure)", .{}), |
| 696 | .success => { | 702 | .success => { |
| 697 | const writer = std.io.getStdErr().writer(); | 703 | const writer = std.io.getStdErr().writer(); |
| 698 | ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR"); | 704 | ctx.dump(module_fn.body, writer) catch @panic("failed to dump AIR"); |
| 699 | }, | 705 | }, |
| 700 | } | 706 | } |
| 701 | } | 707 | } |
| 702 | 708 | ||
| 703 | const DumpTzir = struct { | 709 | const DumpAir = struct { |
| 704 | allocator: *std.mem.Allocator, | 710 | allocator: *std.mem.Allocator, |
| 705 | arena: std.heap.ArenaAllocator, | 711 | arena: std.heap.ArenaAllocator, |
| 706 | old_module: *const Module, | 712 | old_module: *const Module, |
| ... | @@ -718,7 +724,7 @@ const DumpTzir = struct { | ... | @@ -718,7 +724,7 @@ const DumpTzir = struct { |
| 718 | /// TODO: Improve this code to include a stack of Body and store the instructions | 724 | /// TODO: Improve this code to include a stack of Body and store the instructions |
| 719 | /// in there. Now we are putting all the instructions in a function local table, | 725 | /// in there. Now we are putting all the instructions in a function local table, |
| 720 | /// however instructions that are in a Body can be thown away when the Body ends. | 726 | /// however instructions that are in a Body can be thown away when the Body ends. |
| 721 | fn dump(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) !void { | 727 | fn dump(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) !void { |
| 722 | // First pass to pre-populate the table so that we can show even invalid references. | 728 | // First pass to pre-populate the table so that we can show even invalid references. |
| 723 | // Must iterate the same order we iterate the second time. | 729 | // Must iterate the same order we iterate the second time. |
| 724 | // We also look for constants and put them in the const_table. | 730 | // We also look for constants and put them in the const_table. |
| ... | @@ -737,7 +743,7 @@ const DumpTzir = struct { | ... | @@ -737,7 +743,7 @@ const DumpTzir = struct { |
| 737 | return dtz.dumpBody(body, writer); | 743 | return dtz.dumpBody(body, writer); |
| 738 | } | 744 | } |
| 739 | 745 | ||
| 740 | fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void { | 746 | fn fetchInstsAndResolveConsts(dtz: *DumpAir, body: Body) error{OutOfMemory}!void { |
| 741 | for (body.instructions) |inst| { | 747 | for (body.instructions) |inst| { |
| 742 | try dtz.inst_table.put(inst, dtz.next_index); | 748 | try dtz.inst_table.put(inst, dtz.next_index); |
| 743 | dtz.next_index += 1; | 749 | dtz.next_index += 1; |
| ... | @@ -759,7 +765,9 @@ const DumpTzir = struct { | ... | @@ -759,7 +765,9 @@ const DumpTzir = struct { |
| 759 | .is_null, | 765 | .is_null, |
| 760 | .is_null_ptr, | 766 | .is_null_ptr, |
| 761 | .is_err, | 767 | .is_err, |
| 768 | .is_non_err, | ||
| 762 | .is_err_ptr, | 769 | .is_err_ptr, |
| 770 | .is_non_err_ptr, | ||
| 763 | .ptrtoint, | 771 | .ptrtoint, |
| 764 | .floatcast, | 772 | .floatcast, |
| 765 | .intcast, | 773 | .intcast, |
| ... | @@ -865,7 +873,7 @@ const DumpTzir = struct { | ... | @@ -865,7 +873,7 @@ const DumpTzir = struct { |
| 865 | } | 873 | } |
| 866 | } | 874 | } |
| 867 | 875 | ||
| 868 | fn dumpBody(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void { | 876 | fn dumpBody(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void { |
| 869 | for (body.instructions) |inst| { | 877 | for (body.instructions) |inst| { |
| 870 | const my_index = dtz.next_partial_index; | 878 | const my_index = dtz.next_partial_index; |
| 871 | try dtz.partial_inst_table.put(inst, my_index); | 879 | try dtz.partial_inst_table.put(inst, my_index); |
| ... | @@ -888,11 +896,13 @@ const DumpTzir = struct { | ... | @@ -888,11 +896,13 @@ const DumpTzir = struct { |
| 888 | .bitcast, | 896 | .bitcast, |
| 889 | .not, | 897 | .not, |
| 890 | .is_non_null, | 898 | .is_non_null, |
| 891 | .is_null, | ||
| 892 | .is_non_null_ptr, | 899 | .is_non_null_ptr, |
| 900 | .is_null, | ||
| 893 | .is_null_ptr, | 901 | .is_null_ptr, |
| 894 | .is_err, | 902 | .is_err, |
| 895 | .is_err_ptr, | 903 | .is_err_ptr, |
| 904 | .is_non_err, | ||
| 905 | .is_non_err_ptr, | ||
| 896 | .ptrtoint, | 906 | .ptrtoint, |
| 897 | .floatcast, | 907 | .floatcast, |
| 898 | .intcast, | 908 | .intcast, |
| ... | @@ -1150,7 +1160,7 @@ const DumpTzir = struct { | ... | @@ -1150,7 +1160,7 @@ const DumpTzir = struct { |
| 1150 | } | 1160 | } |
| 1151 | } | 1161 | } |
| 1152 | 1162 | ||
| 1153 | fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *Inst) !?usize { | 1163 | fn writeInst(dtz: *DumpAir, writer: std.fs.File.Writer, inst: *Inst) !?usize { |
| 1154 | if (dtz.partial_inst_table.get(inst)) |operand_index| { | 1164 | if (dtz.partial_inst_table.get(inst)) |operand_index| { |
| 1155 | try writer.print("%{d}", .{operand_index}); | 1165 | try writer.print("%{d}", .{operand_index}); |
| 1156 | return null; | 1166 | return null; |
| ... | @@ -1166,7 +1176,7 @@ const DumpTzir = struct { | ... | @@ -1166,7 +1176,7 @@ const DumpTzir = struct { |
| 1166 | } | 1176 | } |
| 1167 | } | 1177 | } |
| 1168 | 1178 | ||
| 1169 | fn findConst(dtz: *DumpTzir, operand: *Inst) !void { | 1179 | fn findConst(dtz: *DumpAir, operand: *Inst) !void { |
| 1170 | if (operand.tag == .constant) { | 1180 | if (operand.tag == .constant) { |
| 1171 | try dtz.const_table.put(operand, dtz.next_const_index); | 1181 | try dtz.const_table.put(operand, dtz.next_const_index); |
| 1172 | dtz.next_const_index += 1; | 1182 | dtz.next_const_index += 1; |
src/codegen.zig+141-85| ... | @@ -142,40 +142,52 @@ pub fn generateSymbol( | ... | @@ -142,40 +142,52 @@ pub fn generateSymbol( |
| 142 | ), | 142 | ), |
| 143 | }; | 143 | }; |
| 144 | }, | 144 | }, |
| 145 | .Pointer => { | 145 | .Pointer => switch (typed_value.ty.ptrSize()) { |
| 146 | // TODO populate .debug_info for the pointer | 146 | .Slice => { |
| 147 | if (typed_value.val.castTag(.decl_ref)) |payload| { | 147 | return Result{ |
| 148 | const decl = payload.data; | 148 | .fail = try ErrorMsg.create( |
| 149 | if (decl.analysis != .complete) return error.AnalysisFail; | 149 | bin_file.allocator, |
| 150 | // TODO handle the dependency of this symbol on the decl's vaddr. | 150 | src_loc, |
| 151 | // If the decl changes vaddr, then this symbol needs to get regenerated. | 151 | "TODO implement generateSymbol for slice {}", |
| 152 | const vaddr = bin_file.getDeclVAddr(decl); | 152 | .{typed_value.val}, |
| 153 | const endian = bin_file.options.target.cpu.arch.endian(); | 153 | ), |
| 154 | switch (bin_file.options.target.cpu.arch.ptrBitWidth()) { | 154 | }; |
| 155 | 16 => { | 155 | }, |
| 156 | try code.resize(2); | 156 | else => { |
| 157 | mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian); | 157 | // TODO populate .debug_info for the pointer |
| 158 | }, | 158 | if (typed_value.val.castTag(.decl_ref)) |payload| { |
| 159 | 32 => { | 159 | const decl = payload.data; |
| 160 | try code.resize(4); | 160 | if (decl.analysis != .complete) return error.AnalysisFail; |
| 161 | mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian); | 161 | // TODO handle the dependency of this symbol on the decl's vaddr. |
| 162 | }, | 162 | // If the decl changes vaddr, then this symbol needs to get regenerated. |
| 163 | 64 => { | 163 | const vaddr = bin_file.getDeclVAddr(decl); |
| 164 | try code.resize(8); | 164 | const endian = bin_file.options.target.cpu.arch.endian(); |
| 165 | mem.writeInt(u64, code.items[0..8], vaddr, endian); | 165 | switch (bin_file.options.target.cpu.arch.ptrBitWidth()) { |
| 166 | }, | 166 | 16 => { |
| 167 | else => unreachable, | 167 | try code.resize(2); |
| 168 | mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian); | ||
| 169 | }, | ||
| 170 | 32 => { | ||
| 171 | try code.resize(4); | ||
| 172 | mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian); | ||
| 173 | }, | ||
| 174 | 64 => { | ||
| 175 | try code.resize(8); | ||
| 176 | mem.writeInt(u64, code.items[0..8], vaddr, endian); | ||
| 177 | }, | ||
| 178 | else => unreachable, | ||
| 179 | } | ||
| 180 | return Result{ .appended = {} }; | ||
| 168 | } | 181 | } |
| 169 | return Result{ .appended = {} }; | 182 | return Result{ |
| 170 | } | 183 | .fail = try ErrorMsg.create( |
| 171 | return Result{ | 184 | bin_file.allocator, |
| 172 | .fail = try ErrorMsg.create( | 185 | src_loc, |
| 173 | bin_file.allocator, | 186 | "TODO implement generateSymbol for pointer {}", |
| 174 | src_loc, | 187 | .{typed_value.val}, |
| 175 | "TODO implement generateSymbol for pointer {}", | 188 | ), |
| 176 | .{typed_value.val}, | 189 | }; |
| 177 | ), | 190 | }, |
| 178 | }; | ||
| 179 | }, | 191 | }, |
| 180 | .Int => { | 192 | .Int => { |
| 181 | // TODO populate .debug_info for the integer | 193 | // TODO populate .debug_info for the integer |
| ... | @@ -847,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -847,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 847 | .is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?), | 859 | .is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?), |
| 848 | .is_null => return self.genIsNull(inst.castTag(.is_null).?), | 860 | .is_null => return self.genIsNull(inst.castTag(.is_null).?), |
| 849 | .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?), | 861 | .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?), |
| 862 | .is_non_err => return self.genIsNonErr(inst.castTag(.is_non_err).?), | ||
| 863 | .is_non_err_ptr => return self.genIsNonErrPtr(inst.castTag(.is_non_err_ptr).?), | ||
| 850 | .is_err => return self.genIsErr(inst.castTag(.is_err).?), | 864 | .is_err => return self.genIsErr(inst.castTag(.is_err).?), |
| 851 | .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?), | 865 | .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?), |
| 852 | .load => return self.genLoad(inst.castTag(.load).?), | 866 | .load => return self.genLoad(inst.castTag(.load).?), |
| ... | @@ -2244,10 +2258,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2244,10 +2258,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2244 | try self.register_manager.getReg(reg, null); | 2258 | try self.register_manager.getReg(reg, null); |
| 2245 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); | 2259 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); |
| 2246 | }, | 2260 | }, |
| 2247 | .stack_offset => { | 2261 | .stack_offset => |off| { |
| 2248 | // Here we need to emit instructions like this: | 2262 | // Here we need to emit instructions like this: |
| 2249 | // mov qword ptr [rsp + stack_offset], x | 2263 | // mov qword ptr [rsp + stack_offset], x |
| 2250 | return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{}); | 2264 | try self.genSetStack(arg.src, arg.ty, off, arg_mcv); |
| 2251 | }, | 2265 | }, |
| 2252 | .ptr_stack_offset => { | 2266 | .ptr_stack_offset => { |
| 2253 | return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{}); | 2267 | return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{}); |
| ... | @@ -2960,6 +2974,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2960,6 +2974,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2960 | return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{}); | 2974 | return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{}); |
| 2961 | } | 2975 | } |
| 2962 | 2976 | ||
| 2977 | fn genIsNonErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | ||
| 2978 | switch (arch) { | ||
| 2979 | else => return self.fail(inst.base.src, "TODO implement is_non_err for {}", .{self.target.cpu.arch}), | ||
| 2980 | } | ||
| 2981 | } | ||
| 2982 | |||
| 2983 | fn genIsNonErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | ||
| 2984 | return self.fail(inst.base.src, "TODO load the operand and call genIsNonErr", .{}); | ||
| 2985 | } | ||
| 2986 | |||
| 2963 | fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue { | 2987 | fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue { |
| 2964 | // A loop is a setup to be able to jump back to the beginning. | 2988 | // A loop is a setup to be able to jump back to the beginning. |
| 2965 | const start_index = self.code.items.len; | 2989 | const start_index = self.code.items.len; |
| ... | @@ -3444,9 +3468,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3444,9 +3468,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3444 | }, | 3468 | }, |
| 3445 | } | 3469 | } |
| 3446 | }, | 3470 | }, |
| 3447 | .embedded_in_code => |code_offset| { | 3471 | .embedded_in_code => { |
| 3448 | _ = code_offset; | 3472 | // TODO this and `.stack_offset` below need to get improved to support types greater than |
| 3449 | return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{}); | 3473 | // register size, and do general memcpy |
| 3474 | const reg = try self.copyToTmpRegister(src, ty, mcv); | ||
| 3475 | return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); | ||
| 3450 | }, | 3476 | }, |
| 3451 | .register => |reg| { | 3477 | .register => |reg| { |
| 3452 | try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89); | 3478 | try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89); |
| ... | @@ -3456,6 +3482,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3456,6 +3482,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3456 | return self.fail(src, "TODO implement set stack variable from memory vaddr", .{}); | 3482 | return self.fail(src, "TODO implement set stack variable from memory vaddr", .{}); |
| 3457 | }, | 3483 | }, |
| 3458 | .stack_offset => |off| { | 3484 | .stack_offset => |off| { |
| 3485 | // TODO this and `.embedded_in_code` above need to get improved to support types greater than | ||
| 3486 | // register size, and do general memcpy | ||
| 3487 | |||
| 3459 | if (stack_offset == off) | 3488 | if (stack_offset == off) |
| 3460 | return; // Copy stack variable to itself; nothing to do. | 3489 | return; // Copy stack variable to itself; nothing to do. |
| 3461 | 3490 | ||
| ... | @@ -4161,33 +4190,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -4161,33 +4190,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4161 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 4190 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 4162 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 4191 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 4163 | switch (typed_value.ty.zigTypeTag()) { | 4192 | switch (typed_value.ty.zigTypeTag()) { |
| 4164 | .Pointer => { | 4193 | .Pointer => switch (typed_value.ty.ptrSize()) { |
| 4165 | if (typed_value.val.castTag(.decl_ref)) |payload| { | 4194 | .Slice => { |
| 4166 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 4195 | var buf: Type.Payload.ElemType = undefined; |
| 4167 | const decl = payload.data; | 4196 | const ptr_type = typed_value.ty.slicePtrFieldType(&buf); |
| 4168 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | 4197 | const ptr_mcv = try self.genTypedValue(src, .{ .ty = ptr_type, .val = typed_value.val }); |
| 4169 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | 4198 | const slice_len = typed_value.val.sliceLen(); |
| 4170 | return MCValue{ .memory = got_addr }; | 4199 | // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean |
| 4171 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | 4200 | // the Sema code needs to use anonymous Decls or alloca instructions to store data. |
| 4172 | const decl = payload.data; | 4201 | const ptr_imm = ptr_mcv.memory; |
| 4173 | const got_addr = blk: { | 4202 | _ = slice_len; |
| 4174 | const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; | 4203 | _ = ptr_imm; |
| 4175 | const got = seg.sections.items[macho_file.got_section_index.?]; | 4204 | // We need more general support for const data being stored in memory to make this work. |
| 4176 | break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes; | 4205 | return self.fail(src, "TODO codegen for const slices", .{}); |
| 4177 | }; | 4206 | }, |
| 4178 | return MCValue{ .memory = got_addr }; | 4207 | else => { |
| 4179 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | 4208 | if (typed_value.val.castTag(.decl_ref)) |payload| { |
| 4180 | const decl = payload.data; | 4209 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 4181 | const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; | 4210 | const decl = payload.data; |
| 4182 | return MCValue{ .memory = got_addr }; | 4211 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; |
| 4183 | } else { | 4212 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; |
| 4184 | return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{}); | 4213 | return MCValue{ .memory = got_addr }; |
| 4214 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | ||
| 4215 | const decl = payload.data; | ||
| 4216 | const got_addr = blk: { | ||
| 4217 | const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; | ||
| 4218 | const got = seg.sections.items[macho_file.got_section_index.?]; | ||
| 4219 | break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes; | ||
| 4220 | }; | ||
| 4221 | return MCValue{ .memory = got_addr }; | ||
| 4222 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | ||
| 4223 | const decl = payload.data; | ||
| 4224 | const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; | ||
| 4225 | return MCValue{ .memory = got_addr }; | ||
| 4226 | } else { | ||
| 4227 | return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{}); | ||
| 4228 | } | ||
| 4185 | } | 4229 | } |
| 4186 | } | 4230 | if (typed_value.val.tag() == .int_u64) { |
| 4187 | if (typed_value.val.tag() == .int_u64) { | 4231 | return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; |
| 4188 | return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; | 4232 | } |
| 4189 | } | 4233 | return self.fail(src, "TODO codegen more kinds of const pointers", .{}); |
| 4190 | return self.fail(src, "TODO codegen more kinds of const pointers", .{}); | 4234 | }, |
| 4191 | }, | 4235 | }, |
| 4192 | .Int => { | 4236 | .Int => { |
| 4193 | const info = typed_value.ty.intInfo(self.target.*); | 4237 | const info = typed_value.ty.intInfo(self.target.*); |
| ... | @@ -4264,27 +4308,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -4264,27 +4308,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4264 | var next_stack_offset: u32 = 0; | 4308 | var next_stack_offset: u32 = 0; |
| 4265 | 4309 | ||
| 4266 | for (param_types) |ty, i| { | 4310 | for (param_types) |ty, i| { |
| 4267 | switch (ty.zigTypeTag()) { | 4311 | if (!ty.hasCodeGenBits()) { |
| 4268 | .Bool, .Int => { | 4312 | assert(cc != .C); |
| 4269 | if (!ty.hasCodeGenBits()) { | 4313 | result.args[i] = .{ .none = {} }; |
| 4270 | assert(cc != .C); | 4314 | continue; |
| 4271 | result.args[i] = .{ .none = {} }; | 4315 | } |
| 4272 | } else { | 4316 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 4273 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | 4317 | const pass_in_reg = switch (ty.zigTypeTag()) { |
| 4274 | if (next_int_reg >= c_abi_int_param_regs.len) { | 4318 | .Bool => true, |
| 4275 | result.args[i] = .{ .stack_offset = next_stack_offset }; | 4319 | .Int => param_size <= 8, |
| 4276 | next_stack_offset += param_size; | 4320 | .Pointer => ty.ptrSize() != .Slice, |
| 4277 | } else { | 4321 | .Optional => ty.isPtrLikeOptional(), |
| 4278 | const aliased_reg = registerAlias( | 4322 | else => false, |
| 4279 | c_abi_int_param_regs[next_int_reg], | 4323 | }; |
| 4280 | param_size, | 4324 | if (pass_in_reg) { |
| 4281 | ); | 4325 | if (next_int_reg >= c_abi_int_param_regs.len) { |
| 4282 | result.args[i] = .{ .register = aliased_reg }; | 4326 | result.args[i] = .{ .stack_offset = next_stack_offset }; |
| 4283 | next_int_reg += 1; | 4327 | next_stack_offset += param_size; |
| 4284 | } | 4328 | } else { |
| 4285 | } | 4329 | const aliased_reg = registerAlias( |
| 4286 | }, | 4330 | c_abi_int_param_regs[next_int_reg], |
| 4287 | else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}), | 4331 | param_size, |
| 4332 | ); | ||
| 4333 | result.args[i] = .{ .register = aliased_reg }; | ||
| 4334 | next_int_reg += 1; | ||
| 4335 | } | ||
| 4336 | } else { | ||
| 4337 | // For simplicity of codegen, slices and other types are always pushed onto the stack. | ||
| 4338 | // TODO: look into optimizing this by passing things as registers sometimes, | ||
| 4339 | // such as ptr and len of slices as separate registers. | ||
| 4340 | // TODO: also we need to honor the C ABI for relevant types rather than passing on | ||
| 4341 | // the stack here. | ||
| 4342 | result.args[i] = .{ .stack_offset = next_stack_offset }; | ||
| 4343 | next_stack_offset += param_size; | ||
| 4288 | } | 4344 | } |
| 4289 | } | 4345 | } |
| 4290 | result.stack_byte_count = next_stack_offset; | 4346 | result.stack_byte_count = next_stack_offset; |
src/codegen/c.zig+285-68| ... | @@ -39,7 +39,12 @@ const BlockData = struct { | ... | @@ -39,7 +39,12 @@ const BlockData = struct { |
| 39 | }; | 39 | }; |
| 40 | 40 | ||
| 41 | pub const CValueMap = std.AutoHashMap(*Inst, CValue); | 41 | pub const CValueMap = std.AutoHashMap(*Inst, CValue); |
| 42 | pub const TypedefMap = std.HashMap(Type, struct { name: []const u8, rendered: []u8 }, Type.HashContext, std.hash_map.default_max_load_percentage); | 42 | pub const TypedefMap = std.HashMap( |
| 43 | Type, | ||
| 44 | struct { name: []const u8, rendered: []u8 }, | ||
| 45 | Type.HashContext, | ||
| 46 | std.hash_map.default_max_load_percentage, | ||
| 47 | ); | ||
| 43 | 48 | ||
| 44 | fn formatTypeAsCIdentifier( | 49 | fn formatTypeAsCIdentifier( |
| 45 | data: Type, | 50 | data: Type, |
| ... | @@ -151,14 +156,49 @@ pub const Object = struct { | ... | @@ -151,14 +156,49 @@ pub const Object = struct { |
| 151 | render_ty = render_ty.elemType(); | 156 | render_ty = render_ty.elemType(); |
| 152 | } | 157 | } |
| 153 | 158 | ||
| 154 | try o.dg.renderType(w, render_ty); | 159 | if (render_ty.zigTypeTag() == .Fn) { |
| 155 | 160 | const ret_ty = render_ty.fnReturnType(); | |
| 156 | const const_prefix = switch (mutability) { | 161 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 157 | .Const => "const ", | 162 | // noreturn attribute is not allowed here. |
| 158 | .Mut => "", | 163 | try w.writeAll("void"); |
| 159 | }; | 164 | } else { |
| 160 | try w.print(" {s}", .{const_prefix}); | 165 | try o.dg.renderType(w, ret_ty); |
| 161 | try o.writeCValue(w, name); | 166 | } |
| 167 | try w.writeAll(" (*"); | ||
| 168 | switch (mutability) { | ||
| 169 | .Const => try w.writeAll("const "), | ||
| 170 | .Mut => {}, | ||
| 171 | } | ||
| 172 | try o.writeCValue(w, name); | ||
| 173 | try w.writeAll(")("); | ||
| 174 | const param_len = render_ty.fnParamLen(); | ||
| 175 | const is_var_args = render_ty.fnIsVarArgs(); | ||
| 176 | if (param_len == 0 and !is_var_args) | ||
| 177 | try w.writeAll("void") | ||
| 178 | else { | ||
| 179 | var index: usize = 0; | ||
| 180 | while (index < param_len) : (index += 1) { | ||
| 181 | if (index > 0) { | ||
| 182 | try w.writeAll(", "); | ||
| 183 | } | ||
| 184 | try o.dg.renderType(w, render_ty.fnParamType(index)); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | if (is_var_args) { | ||
| 188 | if (param_len != 0) try w.writeAll(", "); | ||
| 189 | try w.writeAll("..."); | ||
| 190 | } | ||
| 191 | try w.writeByte(')'); | ||
| 192 | } else { | ||
| 193 | try o.dg.renderType(w, render_ty); | ||
| 194 | |||
| 195 | const const_prefix = switch (mutability) { | ||
| 196 | .Const => "const ", | ||
| 197 | .Mut => "", | ||
| 198 | }; | ||
| 199 | try w.print(" {s}", .{const_prefix}); | ||
| 200 | try o.writeCValue(w, name); | ||
| 201 | } | ||
| 162 | try w.writeAll(suffix.items); | 202 | try w.writeAll(suffix.items); |
| 163 | } | 203 | } |
| 164 | }; | 204 | }; |
| ... | @@ -196,35 +236,72 @@ pub const DeclGen = struct { | ... | @@ -196,35 +236,72 @@ pub const DeclGen = struct { |
| 196 | return writer.print("{d}", .{val.toSignedInt()}); | 236 | return writer.print("{d}", .{val.toSignedInt()}); |
| 197 | return writer.print("{d}", .{val.toUnsignedInt()}); | 237 | return writer.print("{d}", .{val.toUnsignedInt()}); |
| 198 | }, | 238 | }, |
| 199 | .Pointer => switch (val.tag()) { | 239 | .Pointer => switch (t.ptrSize()) { |
| 200 | .null_value, .zero => try writer.writeAll("NULL"), | 240 | .Slice => { |
| 201 | .one => try writer.writeAll("1"), | 241 | try writer.writeByte('('); |
| 202 | .decl_ref => { | 242 | try dg.renderType(writer, t); |
| 203 | const decl = val.castTag(.decl_ref).?.data; | 243 | try writer.writeAll("){"); |
| 204 | 244 | var buf: Type.Payload.ElemType = undefined; | |
| 205 | // Determine if we must pointer cast. | 245 | try dg.renderValue(writer, t.slicePtrFieldType(&buf), val); |
| 206 | assert(decl.has_tv); | 246 | try writer.writeAll(", "); |
| 207 | if (t.eql(decl.ty)) { | 247 | try writer.print("{d}", .{val.sliceLen()}); |
| 208 | try writer.print("&{s}", .{decl.name}); | 248 | try writer.writeAll("}"); |
| 209 | } else { | ||
| 210 | try writer.writeAll("("); | ||
| 211 | try dg.renderType(writer, t); | ||
| 212 | try writer.print(")&{s}", .{decl.name}); | ||
| 213 | } | ||
| 214 | }, | ||
| 215 | .function => { | ||
| 216 | const func = val.castTag(.function).?.data; | ||
| 217 | try writer.print("{s}", .{func.owner_decl.name}); | ||
| 218 | }, | 249 | }, |
| 219 | .extern_fn => { | 250 | else => switch (val.tag()) { |
| 220 | const decl = val.castTag(.extern_fn).?.data; | 251 | .null_value, .zero => try writer.writeAll("NULL"), |
| 221 | try writer.print("{s}", .{decl.name}); | 252 | .one => try writer.writeAll("1"), |
| 253 | .decl_ref => { | ||
| 254 | const decl = val.castTag(.decl_ref).?.data; | ||
| 255 | |||
| 256 | // Determine if we must pointer cast. | ||
| 257 | assert(decl.has_tv); | ||
| 258 | if (t.eql(decl.ty)) { | ||
| 259 | try writer.print("&{s}", .{decl.name}); | ||
| 260 | } else { | ||
| 261 | try writer.writeAll("("); | ||
| 262 | try dg.renderType(writer, t); | ||
| 263 | try writer.print(")&{s}", .{decl.name}); | ||
| 264 | } | ||
| 265 | }, | ||
| 266 | .function => { | ||
| 267 | const func = val.castTag(.function).?.data; | ||
| 268 | try writer.print("{s}", .{func.owner_decl.name}); | ||
| 269 | }, | ||
| 270 | .extern_fn => { | ||
| 271 | const decl = val.castTag(.extern_fn).?.data; | ||
| 272 | try writer.print("{s}", .{decl.name}); | ||
| 273 | }, | ||
| 274 | else => switch (t.ptrSize()) { | ||
| 275 | .Slice => unreachable, | ||
| 276 | .Many => { | ||
| 277 | if (val.castTag(.ref_val)) |ref_val_payload| { | ||
| 278 | const sub_val = ref_val_payload.data; | ||
| 279 | if (sub_val.castTag(.bytes)) |bytes_payload| { | ||
| 280 | const bytes = bytes_payload.data; | ||
| 281 | try writer.writeByte('('); | ||
| 282 | try dg.renderType(writer, t); | ||
| 283 | // TODO: make our own C string escape instead of using std.zig.fmtEscapes | ||
| 284 | try writer.print(")\"{}\"", .{std.zig.fmtEscapes(bytes)}); | ||
| 285 | } else { | ||
| 286 | unreachable; | ||
| 287 | } | ||
| 288 | } else { | ||
| 289 | unreachable; | ||
| 290 | } | ||
| 291 | }, | ||
| 292 | .One => { | ||
| 293 | var arena = std.heap.ArenaAllocator.init(dg.module.gpa); | ||
| 294 | defer arena.deinit(); | ||
| 295 | |||
| 296 | const elem_ty = t.elemType(); | ||
| 297 | const elem_val = try val.pointerDeref(&arena.allocator); | ||
| 298 | |||
| 299 | try writer.writeAll("&"); | ||
| 300 | try dg.renderValue(writer, elem_ty, elem_val); | ||
| 301 | }, | ||
| 302 | .C => unreachable, | ||
| 303 | }, | ||
| 222 | }, | 304 | }, |
| 223 | else => |e| return dg.fail( | ||
| 224 | .{ .node_offset = 0 }, | ||
| 225 | "TODO: C backend: implement Pointer value {s}", | ||
| 226 | .{@tagName(e)}, | ||
| 227 | ), | ||
| 228 | }, | 305 | }, |
| 229 | .Array => { | 306 | .Array => { |
| 230 | // First try specific tag representations for more efficiency. | 307 | // First try specific tag representations for more efficiency. |
| ... | @@ -283,6 +360,12 @@ pub const DeclGen = struct { | ... | @@ -283,6 +360,12 @@ pub const DeclGen = struct { |
| 283 | const error_type = t.errorUnionSet(); | 360 | const error_type = t.errorUnionSet(); |
| 284 | const payload_type = t.errorUnionChild(); | 361 | const payload_type = t.errorUnionChild(); |
| 285 | const data = val.castTag(.error_union).?.data; | 362 | const data = val.castTag(.error_union).?.data; |
| 363 | |||
| 364 | if (!payload_type.hasCodeGenBits()) { | ||
| 365 | // We use the error type directly as the type. | ||
| 366 | return dg.renderValue(writer, error_type, data); | ||
| 367 | } | ||
| 368 | |||
| 286 | try writer.writeByte('('); | 369 | try writer.writeByte('('); |
| 287 | try dg.renderType(writer, t); | 370 | try dg.renderType(writer, t); |
| 288 | try writer.writeAll("){"); | 371 | try writer.writeAll("){"); |
| ... | @@ -329,6 +412,32 @@ pub const DeclGen = struct { | ... | @@ -329,6 +412,32 @@ pub const DeclGen = struct { |
| 329 | }, | 412 | }, |
| 330 | } | 413 | } |
| 331 | }, | 414 | }, |
| 415 | .Fn => switch (val.tag()) { | ||
| 416 | .null_value, .zero => try writer.writeAll("NULL"), | ||
| 417 | .one => try writer.writeAll("1"), | ||
| 418 | .decl_ref => { | ||
| 419 | const decl = val.castTag(.decl_ref).?.data; | ||
| 420 | |||
| 421 | // Determine if we must pointer cast. | ||
| 422 | assert(decl.has_tv); | ||
| 423 | if (t.eql(decl.ty)) { | ||
| 424 | try writer.print("&{s}", .{decl.name}); | ||
| 425 | } else { | ||
| 426 | try writer.writeAll("("); | ||
| 427 | try dg.renderType(writer, t); | ||
| 428 | try writer.print(")&{s}", .{decl.name}); | ||
| 429 | } | ||
| 430 | }, | ||
| 431 | .function => { | ||
| 432 | const func = val.castTag(.function).?.data; | ||
| 433 | try writer.print("{s}", .{func.owner_decl.name}); | ||
| 434 | }, | ||
| 435 | .extern_fn => { | ||
| 436 | const decl = val.castTag(.extern_fn).?.data; | ||
| 437 | try writer.print("{s}", .{decl.name}); | ||
| 438 | }, | ||
| 439 | else => unreachable, | ||
| 440 | }, | ||
| 332 | else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{ | 441 | else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{ |
| 333 | @tagName(e), | 442 | @tagName(e), |
| 334 | }), | 443 | }), |
| ... | @@ -339,6 +448,12 @@ pub const DeclGen = struct { | ... | @@ -339,6 +448,12 @@ pub const DeclGen = struct { |
| 339 | if (!is_global) { | 448 | if (!is_global) { |
| 340 | try w.writeAll("static "); | 449 | try w.writeAll("static "); |
| 341 | } | 450 | } |
| 451 | if (dg.decl.val.castTag(.function)) |func_payload| { | ||
| 452 | const func: *Module.Fn = func_payload.data; | ||
| 453 | if (func.is_cold) { | ||
| 454 | try w.writeAll("ZIG_COLD "); | ||
| 455 | } | ||
| 456 | } | ||
| 342 | try dg.renderType(w, dg.decl.ty.fnReturnType()); | 457 | try dg.renderType(w, dg.decl.ty.fnReturnType()); |
| 343 | const decl_name = mem.span(dg.decl.name); | 458 | const decl_name = mem.span(dg.decl.name); |
| 344 | try w.print(" {s}(", .{decl_name}); | 459 | try w.print(" {s}(", .{decl_name}); |
| ... | @@ -413,7 +528,35 @@ pub const DeclGen = struct { | ... | @@ -413,7 +528,35 @@ pub const DeclGen = struct { |
| 413 | 528 | ||
| 414 | .Pointer => { | 529 | .Pointer => { |
| 415 | if (t.isSlice()) { | 530 | if (t.isSlice()) { |
| 416 | return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement slices", .{}); | 531 | if (dg.typedefs.get(t)) |some| { |
| 532 | return w.writeAll(some.name); | ||
| 533 | } | ||
| 534 | |||
| 535 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 536 | defer buffer.deinit(); | ||
| 537 | const bw = buffer.writer(); | ||
| 538 | |||
| 539 | try bw.writeAll("typedef struct { "); | ||
| 540 | const elem_type = t.elemType(); | ||
| 541 | try dg.renderType(bw, elem_type); | ||
| 542 | try bw.writeAll(" *"); | ||
| 543 | if (t.isConstPtr()) { | ||
| 544 | try bw.writeAll("const "); | ||
| 545 | } | ||
| 546 | if (t.isVolatilePtr()) { | ||
| 547 | try bw.writeAll("volatile "); | ||
| 548 | } | ||
| 549 | try bw.writeAll("ptr; size_t len; } "); | ||
| 550 | const name_index = buffer.items.len; | ||
| 551 | try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)}); | ||
| 552 | |||
| 553 | const rendered = buffer.toOwnedSlice(); | ||
| 554 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 555 | const name = rendered[name_index .. rendered.len - 2]; | ||
| 556 | |||
| 557 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 558 | try w.writeAll(name); | ||
| 559 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | ||
| 417 | } else { | 560 | } else { |
| 418 | try dg.renderType(w, t.elemType()); | 561 | try dg.renderType(w, t.elemType()); |
| 419 | try w.writeAll(" *"); | 562 | try w.writeAll(" *"); |
| ... | @@ -446,13 +589,13 @@ pub const DeclGen = struct { | ... | @@ -446,13 +589,13 @@ pub const DeclGen = struct { |
| 446 | try dg.renderType(bw, child_type); | 589 | try dg.renderType(bw, child_type); |
| 447 | try bw.writeAll(" payload; bool is_null; } "); | 590 | try bw.writeAll(" payload; bool is_null; } "); |
| 448 | const name_index = buffer.items.len; | 591 | const name_index = buffer.items.len; |
| 449 | try bw.print("zig_opt_{s}_t;\n", .{typeToCIdentifier(child_type)}); | 592 | try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)}); |
| 450 | 593 | ||
| 451 | const rendered = buffer.toOwnedSlice(); | 594 | const rendered = buffer.toOwnedSlice(); |
| 452 | errdefer dg.typedefs.allocator.free(rendered); | 595 | errdefer dg.typedefs.allocator.free(rendered); |
| 453 | const name = rendered[name_index .. rendered.len - 2]; | 596 | const name = rendered[name_index .. rendered.len - 2]; |
| 454 | 597 | ||
| 455 | try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); | 598 | try dg.typedefs.ensureUnusedCapacity(1); |
| 456 | try w.writeAll(name); | 599 | try w.writeAll(name); |
| 457 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | 600 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 458 | }, | 601 | }, |
| ... | @@ -465,7 +608,11 @@ pub const DeclGen = struct { | ... | @@ -465,7 +608,11 @@ pub const DeclGen = struct { |
| 465 | return w.writeAll(some.name); | 608 | return w.writeAll(some.name); |
| 466 | } | 609 | } |
| 467 | const child_type = t.errorUnionChild(); | 610 | const child_type = t.errorUnionChild(); |
| 468 | const set_type = t.errorUnionSet(); | 611 | const err_set_type = t.errorUnionSet(); |
| 612 | |||
| 613 | if (!child_type.hasCodeGenBits()) { | ||
| 614 | return dg.renderType(w, err_set_type); | ||
| 615 | } | ||
| 469 | 616 | ||
| 470 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | 617 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); |
| 471 | defer buffer.deinit(); | 618 | defer buffer.deinit(); |
| ... | @@ -475,13 +622,20 @@ pub const DeclGen = struct { | ... | @@ -475,13 +622,20 @@ pub const DeclGen = struct { |
| 475 | try dg.renderType(bw, child_type); | 622 | try dg.renderType(bw, child_type); |
| 476 | try bw.writeAll(" payload; uint16_t error; } "); | 623 | try bw.writeAll(" payload; uint16_t error; } "); |
| 477 | const name_index = buffer.items.len; | 624 | const name_index = buffer.items.len; |
| 478 | try bw.print("zig_err_union_{s}_{s}_t;\n", .{ typeToCIdentifier(set_type), typeToCIdentifier(child_type) }); | 625 | if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| { |
| 626 | const func = inf_err_set_payload.data.func; | ||
| 627 | try bw.print("zig_E_{s};\n", .{func.owner_decl.name}); | ||
| 628 | } else { | ||
| 629 | try bw.print("zig_E_{s}_{s};\n", .{ | ||
| 630 | typeToCIdentifier(err_set_type), typeToCIdentifier(child_type), | ||
| 631 | }); | ||
| 632 | } | ||
| 479 | 633 | ||
| 480 | const rendered = buffer.toOwnedSlice(); | 634 | const rendered = buffer.toOwnedSlice(); |
| 481 | errdefer dg.typedefs.allocator.free(rendered); | 635 | errdefer dg.typedefs.allocator.free(rendered); |
| 482 | const name = rendered[name_index .. rendered.len - 2]; | 636 | const name = rendered[name_index .. rendered.len - 2]; |
| 483 | 637 | ||
| 484 | try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); | 638 | try dg.typedefs.ensureUnusedCapacity(1); |
| 485 | try w.writeAll(name); | 639 | try w.writeAll(name); |
| 486 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | 640 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 487 | }, | 641 | }, |
| ... | @@ -514,7 +668,7 @@ pub const DeclGen = struct { | ... | @@ -514,7 +668,7 @@ pub const DeclGen = struct { |
| 514 | errdefer dg.typedefs.allocator.free(rendered); | 668 | errdefer dg.typedefs.allocator.free(rendered); |
| 515 | const name = rendered[name_start .. rendered.len - 2]; | 669 | const name = rendered[name_start .. rendered.len - 2]; |
| 516 | 670 | ||
| 517 | try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); | 671 | try dg.typedefs.ensureUnusedCapacity(1); |
| 518 | try w.writeAll(name); | 672 | try w.writeAll(name); |
| 519 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | 673 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 520 | }, | 674 | }, |
| ... | @@ -526,7 +680,28 @@ pub const DeclGen = struct { | ... | @@ -526,7 +680,28 @@ pub const DeclGen = struct { |
| 526 | try dg.renderType(w, int_tag_ty); | 680 | try dg.renderType(w, int_tag_ty); |
| 527 | }, | 681 | }, |
| 528 | .Union => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Union", .{}), | 682 | .Union => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Union", .{}), |
| 529 | .Fn => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Fn", .{}), | 683 | .Fn => { |
| 684 | try dg.renderType(w, t.fnReturnType()); | ||
| 685 | try w.writeAll(" (*)("); | ||
| 686 | const param_len = t.fnParamLen(); | ||
| 687 | const is_var_args = t.fnIsVarArgs(); | ||
| 688 | if (param_len == 0 and !is_var_args) | ||
| 689 | try w.writeAll("void") | ||
| 690 | else { | ||
| 691 | var index: usize = 0; | ||
| 692 | while (index < param_len) : (index += 1) { | ||
| 693 | if (index > 0) { | ||
| 694 | try w.writeAll(", "); | ||
| 695 | } | ||
| 696 | try dg.renderType(w, t.fnParamType(index)); | ||
| 697 | } | ||
| 698 | } | ||
| 699 | if (is_var_args) { | ||
| 700 | if (param_len != 0) try w.writeAll(", "); | ||
| 701 | try w.writeAll("..."); | ||
| 702 | } | ||
| 703 | try w.writeByte(')'); | ||
| 704 | }, | ||
| 530 | .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}), | 705 | .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}), |
| 531 | .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}), | 706 | .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}), |
| 532 | .AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}), | 707 | .AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}), |
| ... | @@ -569,23 +744,27 @@ pub fn genDecl(o: *Object) !void { | ... | @@ -569,23 +744,27 @@ pub fn genDecl(o: *Object) !void { |
| 569 | .val = o.dg.decl.val, | 744 | .val = o.dg.decl.val, |
| 570 | }; | 745 | }; |
| 571 | if (tv.val.castTag(.function)) |func_payload| { | 746 | if (tv.val.castTag(.function)) |func_payload| { |
| 572 | const is_global = o.dg.declIsGlobal(tv); | ||
| 573 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | ||
| 574 | if (is_global) { | ||
| 575 | try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); | ||
| 576 | } | ||
| 577 | try o.dg.renderFunctionSignature(fwd_decl_writer, is_global); | ||
| 578 | try fwd_decl_writer.writeAll(";\n"); | ||
| 579 | |||
| 580 | const func: *Module.Fn = func_payload.data; | 747 | const func: *Module.Fn = func_payload.data; |
| 581 | try o.indent_writer.insertNewline(); | 748 | if (func.owner_decl == o.dg.decl) { |
| 582 | try o.dg.renderFunctionSignature(o.writer(), is_global); | 749 | const is_global = o.dg.declIsGlobal(tv); |
| 750 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | ||
| 751 | if (is_global) { | ||
| 752 | try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); | ||
| 753 | } | ||
| 754 | try o.dg.renderFunctionSignature(fwd_decl_writer, is_global); | ||
| 755 | try fwd_decl_writer.writeAll(";\n"); | ||
| 583 | 756 | ||
| 584 | try o.writer().writeByte(' '); | 757 | try o.indent_writer.insertNewline(); |
| 585 | try genBody(o, func.body); | 758 | try o.dg.renderFunctionSignature(o.writer(), is_global); |
| 586 | 759 | ||
| 587 | try o.indent_writer.insertNewline(); | 760 | try o.writer().writeByte(' '); |
| 588 | } else if (tv.val.tag() == .extern_fn) { | 761 | try genBody(o, func.body); |
| 762 | |||
| 763 | try o.indent_writer.insertNewline(); | ||
| 764 | return; | ||
| 765 | } | ||
| 766 | } | ||
| 767 | if (tv.val.tag() == .extern_fn) { | ||
| 589 | const writer = o.writer(); | 768 | const writer = o.writer(); |
| 590 | try writer.writeAll("ZIG_EXTERN_C "); | 769 | try writer.writeAll("ZIG_EXTERN_C "); |
| 591 | try o.dg.renderFunctionSignature(writer, true); | 770 | try o.dg.renderFunctionSignature(writer, true); |
| ... | @@ -644,9 +823,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { | ... | @@ -644,9 +823,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { |
| 644 | const is_global = dg.declIsGlobal(tv); | 823 | const is_global = dg.declIsGlobal(tv); |
| 645 | if (is_global) { | 824 | if (is_global) { |
| 646 | try writer.writeAll("ZIG_EXTERN_C "); | 825 | try writer.writeAll("ZIG_EXTERN_C "); |
| 826 | try dg.renderFunctionSignature(writer, is_global); | ||
| 827 | try dg.fwd_decl.appendSlice(";\n"); | ||
| 647 | } | 828 | } |
| 648 | try dg.renderFunctionSignature(writer, is_global); | ||
| 649 | try dg.fwd_decl.appendSlice(";\n"); | ||
| 650 | }, | 829 | }, |
| 651 | else => {}, | 830 | else => {}, |
| 652 | } | 831 | } |
| ... | @@ -726,8 +905,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi | ... | @@ -726,8 +905,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi |
| 726 | .ref => try genRef(o, inst.castTag(.ref).?), | 905 | .ref => try genRef(o, inst.castTag(.ref).?), |
| 727 | .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?), | 906 | .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?), |
| 728 | 907 | ||
| 729 | .is_err => try genIsErr(o, inst.castTag(.is_err).?), | 908 | .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", ".", "!="), |
| 730 | .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?), | 909 | .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", ".", "=="), |
| 910 | .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "*", "->", "!="), | ||
| 911 | .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "*", "->", "=="), | ||
| 731 | 912 | ||
| 732 | .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), | 913 | .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), |
| 733 | .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), | 914 | .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), |
| ... | @@ -1213,9 +1394,25 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue { | ... | @@ -1213,9 +1394,25 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue { |
| 1213 | 1394 | ||
| 1214 | // *(E!T) -> E NOT *E | 1395 | // *(E!T) -> E NOT *E |
| 1215 | fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { | 1396 | fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { |
| 1397 | if (inst.base.isUnused()) | ||
| 1398 | return CValue.none; | ||
| 1399 | |||
| 1216 | const writer = o.writer(); | 1400 | const writer = o.writer(); |
| 1217 | const operand = try o.resolveInst(inst.operand); | 1401 | const operand = try o.resolveInst(inst.operand); |
| 1218 | 1402 | ||
| 1403 | const payload_ty = inst.operand.ty.errorUnionChild(); | ||
| 1404 | if (!payload_ty.hasCodeGenBits()) { | ||
| 1405 | if (inst.operand.ty.zigTypeTag() == .Pointer) { | ||
| 1406 | const local = try o.allocLocal(inst.base.ty, .Const); | ||
| 1407 | try writer.writeAll(" = *"); | ||
| 1408 | try o.writeCValue(writer, operand); | ||
| 1409 | try writer.writeAll(";\n"); | ||
| 1410 | return local; | ||
| 1411 | } else { | ||
| 1412 | return operand; | ||
| 1413 | } | ||
| 1414 | } | ||
| 1415 | |||
| 1219 | const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; | 1416 | const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; |
| 1220 | 1417 | ||
| 1221 | const local = try o.allocLocal(inst.base.ty, .Const); | 1418 | const local = try o.allocLocal(inst.base.ty, .Const); |
| ... | @@ -1225,10 +1422,19 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { | ... | @@ -1225,10 +1422,19 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { |
| 1225 | try writer.print("){s}error;\n", .{maybe_deref}); | 1422 | try writer.print("){s}error;\n", .{maybe_deref}); |
| 1226 | return local; | 1423 | return local; |
| 1227 | } | 1424 | } |
| 1425 | |||
| 1228 | fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { | 1426 | fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { |
| 1427 | if (inst.base.isUnused()) | ||
| 1428 | return CValue.none; | ||
| 1429 | |||
| 1229 | const writer = o.writer(); | 1430 | const writer = o.writer(); |
| 1230 | const operand = try o.resolveInst(inst.operand); | 1431 | const operand = try o.resolveInst(inst.operand); |
| 1231 | 1432 | ||
| 1433 | const payload_ty = inst.operand.ty.errorUnionChild(); | ||
| 1434 | if (!payload_ty.hasCodeGenBits()) { | ||
| 1435 | return CValue.none; | ||
| 1436 | } | ||
| 1437 | |||
| 1232 | const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; | 1438 | const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; |
| 1233 | const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else ""; | 1439 | const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else ""; |
| 1234 | 1440 | ||
| ... | @@ -1277,15 +1483,26 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { | ... | @@ -1277,15 +1483,26 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { |
| 1277 | return local; | 1483 | return local; |
| 1278 | } | 1484 | } |
| 1279 | 1485 | ||
| 1280 | fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue { | 1486 | fn genIsErr( |
| 1487 | o: *Object, | ||
| 1488 | inst: *Inst.UnOp, | ||
| 1489 | deref_prefix: [*:0]const u8, | ||
| 1490 | deref_suffix: [*:0]const u8, | ||
| 1491 | op_str: [*:0]const u8, | ||
| 1492 | ) !CValue { | ||
| 1281 | const writer = o.writer(); | 1493 | const writer = o.writer(); |
| 1282 | const maybe_deref = if (inst.base.tag == .is_err_ptr) "[0]" else ""; | ||
| 1283 | const operand = try o.resolveInst(inst.operand); | 1494 | const operand = try o.resolveInst(inst.operand); |
| 1284 | |||
| 1285 | const local = try o.allocLocal(Type.initTag(.bool), .Const); | 1495 | const local = try o.allocLocal(Type.initTag(.bool), .Const); |
| 1286 | try writer.writeAll(" = ("); | 1496 | const payload_ty = inst.operand.ty.errorUnionChild(); |
| 1287 | try o.writeCValue(writer, operand); | 1497 | if (!payload_ty.hasCodeGenBits()) { |
| 1288 | try writer.print("){s}.error != 0;\n", .{maybe_deref}); | 1498 | try writer.print(" = {s}", .{deref_prefix}); |
| 1499 | try o.writeCValue(writer, operand); | ||
| 1500 | try writer.print(" {s} 0;\n", .{op_str}); | ||
| 1501 | } else { | ||
| 1502 | try writer.writeAll(" = "); | ||
| 1503 | try o.writeCValue(writer, operand); | ||
| 1504 | try writer.print("{s}error {s} 0;\n", .{ deref_suffix, op_str }); | ||
| 1505 | } | ||
| 1289 | return local; | 1506 | return local; |
| 1290 | } | 1507 | } |
| 1291 | 1508 |
src/codegen/wasm.zig+4-5| ... | @@ -814,7 +814,8 @@ pub const Context = struct { | ... | @@ -814,7 +814,8 @@ pub const Context = struct { |
| 814 | .constant => unreachable, | 814 | .constant => unreachable, |
| 815 | .dbg_stmt => WValue.none, | 815 | .dbg_stmt => WValue.none, |
| 816 | .div => self.genBinOp(inst.castTag(.div).?, .div), | 816 | .div => self.genBinOp(inst.castTag(.div).?, .div), |
| 817 | .is_err => self.genIsErr(inst.castTag(.is_err).?), | 817 | .is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne), |
| 818 | .is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq), | ||
| 818 | .load => self.genLoad(inst.castTag(.load).?), | 819 | .load => self.genLoad(inst.castTag(.load).?), |
| 819 | .loop => self.genLoop(inst.castTag(.loop).?), | 820 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 820 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), | 821 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), |
| ... | @@ -1278,7 +1279,7 @@ pub const Context = struct { | ... | @@ -1278,7 +1279,7 @@ pub const Context = struct { |
| 1278 | return .none; | 1279 | return .none; |
| 1279 | } | 1280 | } |
| 1280 | 1281 | ||
| 1281 | fn genIsErr(self: *Context, inst: *Inst.UnOp) InnerError!WValue { | 1282 | fn genIsErr(self: *Context, inst: *Inst.UnOp, opcode: wasm.Opcode) InnerError!WValue { |
| 1282 | const operand = self.resolveInst(inst.operand); | 1283 | const operand = self.resolveInst(inst.operand); |
| 1283 | const offset = self.code.items.len; | 1284 | const offset = self.code.items.len; |
| 1284 | const writer = self.code.writer(); | 1285 | const writer = self.code.writer(); |
| ... | @@ -1289,9 +1290,7 @@ pub const Context = struct { | ... | @@ -1289,9 +1290,7 @@ pub const Context = struct { |
| 1289 | try writer.writeByte(wasm.opcode(.i32_const)); | 1290 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 1290 | try leb.writeILEB128(writer, @as(i32, 0)); | 1291 | try leb.writeILEB128(writer, @as(i32, 0)); |
| 1291 | 1292 | ||
| 1292 | // we want to break out of the condition if they're *not* equal, | 1293 | try writer.writeByte(@enumToInt(opcode)); |
| 1293 | // because that means there's an error. | ||
| 1294 | try writer.writeByte(wasm.opcode(.i32_ne)); | ||
| 1295 | 1294 | ||
| 1296 | return WValue{ .code_offset = offset }; | 1295 | return WValue{ .code_offset = offset }; |
| 1297 | } | 1296 | } |
src/link/C.zig+4-6| ... | @@ -207,7 +207,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { | ... | @@ -207,7 +207,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | var fn_count: usize = 0; | 209 | var fn_count: usize = 0; |
| 210 | var typedefs = std.HashMap(Type, []const u8, Type.HashContext, std.hash_map.default_max_load_percentage).init(comp.gpa); | 210 | var typedefs = std.HashMap(Type, void, Type.HashContext, std.hash_map.default_max_load_percentage).init(comp.gpa); |
| 211 | defer typedefs.deinit(); | 211 | defer typedefs.deinit(); |
| 212 | 212 | ||
| 213 | // Typedefs, forward decls and non-functions first. | 213 | // Typedefs, forward decls and non-functions first. |
| ... | @@ -217,14 +217,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { | ... | @@ -217,14 +217,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 217 | if (!decl.has_tv) continue; | 217 | if (!decl.has_tv) continue; |
| 218 | const buf = buf: { | 218 | const buf = buf: { |
| 219 | if (decl.val.castTag(.function)) |_| { | 219 | if (decl.val.castTag(.function)) |_| { |
| 220 | try typedefs.ensureUnusedCapacity(decl.fn_link.c.typedefs.count()); | ||
| 220 | var it = decl.fn_link.c.typedefs.iterator(); | 221 | var it = decl.fn_link.c.typedefs.iterator(); |
| 221 | while (it.next()) |new| { | 222 | while (it.next()) |new| { |
| 222 | if (typedefs.get(new.key_ptr.*)) |previous| { | 223 | const gop = typedefs.getOrPutAssumeCapacity(new.key_ptr.*); |
| 223 | try err_typedef_writer.print("typedef {s} {s};\n", .{ previous, new.value_ptr.name }); | 224 | if (!gop.found_existing) { |
| 224 | } else { | ||
| 225 | try typedefs.ensureCapacity(typedefs.capacity() + 1); | ||
| 226 | try err_typedef_writer.writeAll(new.value_ptr.rendered); | 225 | try err_typedef_writer.writeAll(new.value_ptr.rendered); |
| 227 | typedefs.putAssumeCapacityNoClobber(new.key_ptr.*, new.value_ptr.name); | ||
| 228 | } | 226 | } |
| 229 | } | 227 | } |
| 230 | fn_count += 1; | 228 | fn_count += 1; |
src/link/C/zig.h+6| ... | @@ -12,6 +12,12 @@ | ... | @@ -12,6 +12,12 @@ |
| 12 | #define zig_threadlocal zig_threadlocal_unavailable | 12 | #define zig_threadlocal zig_threadlocal_unavailable |
| 13 | #endif | 13 | #endif |
| 14 | 14 | ||
| 15 | #if __GNUC__ | ||
| 16 | #define ZIG_COLD __attribute__ ((cold)) | ||
| 17 | #else | ||
| 18 | #define ZIG_COLD | ||
| 19 | #endif | ||
| 20 | |||
| 15 | #if __STDC_VERSION__ >= 199901L | 21 | #if __STDC_VERSION__ >= 199901L |
| 16 | #define ZIG_RESTRICT restrict | 22 | #define ZIG_RESTRICT restrict |
| 17 | #elif defined(__GNUC__) | 23 | #elif defined(__GNUC__) |
src/link/Elf.zig+17-6| ... | @@ -2505,11 +2505,7 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo | ... | @@ -2505,11 +2505,7 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo |
| 2505 | abbrev_base_type, | 2505 | abbrev_base_type, |
| 2506 | DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1 | 2506 | DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1 |
| 2507 | 1, // DW.AT_byte_size, DW.FORM_data1 | 2507 | 1, // DW.AT_byte_size, DW.FORM_data1 |
| 2508 | 'b', | 2508 | 'b', 'o', 'o', 'l', 0, // DW.AT_name, DW.FORM_string |
| 2509 | 'o', | ||
| 2510 | 'o', | ||
| 2511 | 'l', | ||
| 2512 | 0, // DW.AT_name, DW.FORM_string | ||
| 2513 | }); | 2509 | }); |
| 2514 | }, | 2510 | }, |
| 2515 | .Int => { | 2511 | .Int => { |
| ... | @@ -2526,8 +2522,23 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo | ... | @@ -2526,8 +2522,23 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo |
| 2526 | // DW.AT_name, DW.FORM_string | 2522 | // DW.AT_name, DW.FORM_string |
| 2527 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | 2523 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); |
| 2528 | }, | 2524 | }, |
| 2525 | .Optional => { | ||
| 2526 | if (ty.isPtrLikeOptional()) { | ||
| 2527 | try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12); | ||
| 2528 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); | ||
| 2529 | // DW.AT_encoding, DW.FORM_data1 | ||
| 2530 | dbg_info_buffer.appendAssumeCapacity(DW.ATE_address); | ||
| 2531 | // DW.AT_byte_size, DW.FORM_data1 | ||
| 2532 | dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target))); | ||
| 2533 | // DW.AT_name, DW.FORM_string | ||
| 2534 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); | ||
| 2535 | } else { | ||
| 2536 | log.err("TODO implement .debug_info for type '{}'", .{ty}); | ||
| 2537 | try dbg_info_buffer.append(abbrev_pad1); | ||
| 2538 | } | ||
| 2539 | }, | ||
| 2529 | else => { | 2540 | else => { |
| 2530 | std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty}); | 2541 | log.err("TODO implement .debug_info for type '{}'", .{ty}); |
| 2531 | try dbg_info_buffer.append(abbrev_pad1); | 2542 | try dbg_info_buffer.append(abbrev_pad1); |
| 2532 | }, | 2543 | }, |
| 2533 | } | 2544 | } |
src/type.zig+82-3| ... | @@ -58,7 +58,7 @@ pub const Type = extern union { | ... | @@ -58,7 +58,7 @@ pub const Type = extern union { |
| 58 | .bool => return .Bool, | 58 | .bool => return .Bool, |
| 59 | .void => return .Void, | 59 | .void => return .Void, |
| 60 | .type => return .Type, | 60 | .type => return .Type, |
| 61 | .error_set, .error_set_single, .anyerror => return .ErrorSet, | 61 | .error_set, .error_set_single, .anyerror, .error_set_inferred => return .ErrorSet, |
| 62 | .comptime_int => return .ComptimeInt, | 62 | .comptime_int => return .ComptimeInt, |
| 63 | .comptime_float => return .ComptimeFloat, | 63 | .comptime_float => return .ComptimeFloat, |
| 64 | .noreturn => return .NoReturn, | 64 | .noreturn => return .NoReturn, |
| ... | @@ -689,7 +689,15 @@ pub const Type = extern union { | ... | @@ -689,7 +689,15 @@ pub const Type = extern union { |
| 689 | .optional_single_mut_pointer, | 689 | .optional_single_mut_pointer, |
| 690 | .optional_single_const_pointer, | 690 | .optional_single_const_pointer, |
| 691 | .anyframe_T, | 691 | .anyframe_T, |
| 692 | => return self.copyPayloadShallow(allocator, Payload.ElemType), | 692 | => { |
| 693 | const payload = self.cast(Payload.ElemType).?; | ||
| 694 | const new_payload = try allocator.create(Payload.ElemType); | ||
| 695 | new_payload.* = .{ | ||
| 696 | .base = .{ .tag = payload.base.tag }, | ||
| 697 | .data = try payload.data.copy(allocator), | ||
| 698 | }; | ||
| 699 | return Type{ .ptr_otherwise = &new_payload.base }; | ||
| 700 | }, | ||
| 693 | 701 | ||
| 694 | .int_signed, | 702 | .int_signed, |
| 695 | .int_unsigned, | 703 | .int_unsigned, |
| ... | @@ -756,6 +764,7 @@ pub const Type = extern union { | ... | @@ -756,6 +764,7 @@ pub const Type = extern union { |
| 756 | }); | 764 | }); |
| 757 | }, | 765 | }, |
| 758 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), | 766 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), |
| 767 | .error_set_inferred => return self.copyPayloadShallow(allocator, Payload.ErrorSetInferred), | ||
| 759 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), | 768 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), |
| 760 | .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope), | 769 | .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope), |
| 761 | .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), | 770 | .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), |
| ... | @@ -1031,6 +1040,10 @@ pub const Type = extern union { | ... | @@ -1031,6 +1040,10 @@ pub const Type = extern union { |
| 1031 | const error_set = ty.castTag(.error_set).?.data; | 1040 | const error_set = ty.castTag(.error_set).?.data; |
| 1032 | return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name)); | 1041 | return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name)); |
| 1033 | }, | 1042 | }, |
| 1043 | .error_set_inferred => { | ||
| 1044 | const func = ty.castTag(.error_set_inferred).?.data.func; | ||
| 1045 | return writer.print("(inferred error set of {s})", .{func.owner_decl.name}); | ||
| 1046 | }, | ||
| 1034 | .error_set_single => { | 1047 | .error_set_single => { |
| 1035 | const name = ty.castTag(.error_set_single).?.data; | 1048 | const name = ty.castTag(.error_set_single).?.data; |
| 1036 | return writer.print("error{{{s}}}", .{name}); | 1049 | return writer.print("error{{{s}}}", .{name}); |
| ... | @@ -1144,6 +1157,7 @@ pub const Type = extern union { | ... | @@ -1144,6 +1157,7 @@ pub const Type = extern union { |
| 1144 | .anyerror_void_error_union, | 1157 | .anyerror_void_error_union, |
| 1145 | .error_set, | 1158 | .error_set, |
| 1146 | .error_set_single, | 1159 | .error_set_single, |
| 1160 | .error_set_inferred, | ||
| 1147 | .manyptr_u8, | 1161 | .manyptr_u8, |
| 1148 | .manyptr_const_u8, | 1162 | .manyptr_const_u8, |
| 1149 | .atomic_ordering, | 1163 | .atomic_ordering, |
| ... | @@ -1161,6 +1175,9 @@ pub const Type = extern union { | ... | @@ -1161,6 +1175,9 @@ pub const Type = extern union { |
| 1161 | .@"struct" => { | 1175 | .@"struct" => { |
| 1162 | // TODO introduce lazy value mechanism | 1176 | // TODO introduce lazy value mechanism |
| 1163 | const struct_obj = self.castTag(.@"struct").?.data; | 1177 | const struct_obj = self.castTag(.@"struct").?.data; |
| 1178 | assert(struct_obj.status == .have_field_types or | ||
| 1179 | struct_obj.status == .layout_wip or | ||
| 1180 | struct_obj.status == .have_layout); | ||
| 1164 | for (struct_obj.fields.values()) |value| { | 1181 | for (struct_obj.fields.values()) |value| { |
| 1165 | if (value.ty.hasCodeGenBits()) | 1182 | if (value.ty.hasCodeGenBits()) |
| 1166 | return true; | 1183 | return true; |
| ... | @@ -1348,6 +1365,7 @@ pub const Type = extern union { | ... | @@ -1348,6 +1365,7 @@ pub const Type = extern union { |
| 1348 | .error_set_single, | 1365 | .error_set_single, |
| 1349 | .anyerror_void_error_union, | 1366 | .anyerror_void_error_union, |
| 1350 | .anyerror, | 1367 | .anyerror, |
| 1368 | .error_set_inferred, | ||
| 1351 | => return 2, // TODO revisit this when we have the concept of the error tag type | 1369 | => return 2, // TODO revisit this when we have the concept of the error tag type |
| 1352 | 1370 | ||
| 1353 | .array, .array_sentinel => return self.elemType().abiAlignment(target), | 1371 | .array, .array_sentinel => return self.elemType().abiAlignment(target), |
| ... | @@ -1580,6 +1598,7 @@ pub const Type = extern union { | ... | @@ -1580,6 +1598,7 @@ pub const Type = extern union { |
| 1580 | .error_set_single, | 1598 | .error_set_single, |
| 1581 | .anyerror_void_error_union, | 1599 | .anyerror_void_error_union, |
| 1582 | .anyerror, | 1600 | .anyerror, |
| 1601 | .error_set_inferred, | ||
| 1583 | => return 2, // TODO revisit this when we have the concept of the error tag type | 1602 | => return 2, // TODO revisit this when we have the concept of the error tag type |
| 1584 | 1603 | ||
| 1585 | .int_signed, .int_unsigned => { | 1604 | .int_signed, .int_unsigned => { |
| ... | @@ -1744,6 +1763,7 @@ pub const Type = extern union { | ... | @@ -1744,6 +1763,7 @@ pub const Type = extern union { |
| 1744 | .error_set_single, | 1763 | .error_set_single, |
| 1745 | .anyerror_void_error_union, | 1764 | .anyerror_void_error_union, |
| 1746 | .anyerror, | 1765 | .anyerror, |
| 1766 | .error_set_inferred, | ||
| 1747 | => return 16, // TODO revisit this when we have the concept of the error tag type | 1767 | => return 16, // TODO revisit this when we have the concept of the error tag type |
| 1748 | 1768 | ||
| 1749 | .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data, | 1769 | .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data, |
| ... | @@ -1863,6 +1883,48 @@ pub const Type = extern union { | ... | @@ -1863,6 +1883,48 @@ pub const Type = extern union { |
| 1863 | }; | 1883 | }; |
| 1864 | } | 1884 | } |
| 1865 | 1885 | ||
| 1886 | pub fn slicePtrFieldType(self: Type, buffer: *Payload.ElemType) Type { | ||
| 1887 | switch (self.tag()) { | ||
| 1888 | .const_slice_u8 => return Type.initTag(.manyptr_const_u8), | ||
| 1889 | |||
| 1890 | .const_slice => { | ||
| 1891 | const elem_type = self.castTag(.const_slice).?.data; | ||
| 1892 | buffer.* = .{ | ||
| 1893 | .base = .{ .tag = .many_const_pointer }, | ||
| 1894 | .data = elem_type, | ||
| 1895 | }; | ||
| 1896 | return Type.initPayload(&buffer.base); | ||
| 1897 | }, | ||
| 1898 | .mut_slice => { | ||
| 1899 | const elem_type = self.castTag(.mut_slice).?.data; | ||
| 1900 | buffer.* = .{ | ||
| 1901 | .base = .{ .tag = .many_mut_pointer }, | ||
| 1902 | .data = elem_type, | ||
| 1903 | }; | ||
| 1904 | return Type.initPayload(&buffer.base); | ||
| 1905 | }, | ||
| 1906 | |||
| 1907 | .pointer => { | ||
| 1908 | const payload = self.castTag(.pointer).?.data; | ||
| 1909 | assert(payload.size == .Slice); | ||
| 1910 | if (payload.mutable) { | ||
| 1911 | buffer.* = .{ | ||
| 1912 | .base = .{ .tag = .many_mut_pointer }, | ||
| 1913 | .data = payload.pointee_type, | ||
| 1914 | }; | ||
| 1915 | } else { | ||
| 1916 | buffer.* = .{ | ||
| 1917 | .base = .{ .tag = .many_const_pointer }, | ||
| 1918 | .data = payload.pointee_type, | ||
| 1919 | }; | ||
| 1920 | } | ||
| 1921 | return Type.initPayload(&buffer.base); | ||
| 1922 | }, | ||
| 1923 | |||
| 1924 | else => unreachable, | ||
| 1925 | } | ||
| 1926 | } | ||
| 1927 | |||
| 1866 | pub fn isConstPtr(self: Type) bool { | 1928 | pub fn isConstPtr(self: Type) bool { |
| 1867 | return switch (self.tag()) { | 1929 | return switch (self.tag()) { |
| 1868 | .single_const_pointer, | 1930 | .single_const_pointer, |
| ... | @@ -1915,7 +1977,10 @@ pub const Type = extern union { | ... | @@ -1915,7 +1977,10 @@ pub const Type = extern union { |
| 1915 | /// Asserts that the type is an optional | 1977 | /// Asserts that the type is an optional |
| 1916 | pub fn isPtrLikeOptional(self: Type) bool { | 1978 | pub fn isPtrLikeOptional(self: Type) bool { |
| 1917 | switch (self.tag()) { | 1979 | switch (self.tag()) { |
| 1918 | .optional_single_const_pointer, .optional_single_mut_pointer => return true, | 1980 | .optional_single_const_pointer, |
| 1981 | .optional_single_mut_pointer, | ||
| 1982 | => return true, | ||
| 1983 | |||
| 1919 | .optional => { | 1984 | .optional => { |
| 1920 | var buf: Payload.ElemType = undefined; | 1985 | var buf: Payload.ElemType = undefined; |
| 1921 | const child_type = self.optionalChild(&buf); | 1986 | const child_type = self.optionalChild(&buf); |
| ... | @@ -2400,6 +2465,7 @@ pub const Type = extern union { | ... | @@ -2400,6 +2465,7 @@ pub const Type = extern union { |
| 2400 | .error_union, | 2465 | .error_union, |
| 2401 | .error_set, | 2466 | .error_set, |
| 2402 | .error_set_single, | 2467 | .error_set_single, |
| 2468 | .error_set_inferred, | ||
| 2403 | .@"opaque", | 2469 | .@"opaque", |
| 2404 | .var_args_param, | 2470 | .var_args_param, |
| 2405 | .manyptr_u8, | 2471 | .manyptr_u8, |
| ... | @@ -2892,6 +2958,8 @@ pub const Type = extern union { | ... | @@ -2892,6 +2958,8 @@ pub const Type = extern union { |
| 2892 | anyframe_T, | 2958 | anyframe_T, |
| 2893 | error_set, | 2959 | error_set, |
| 2894 | error_set_single, | 2960 | error_set_single, |
| 2961 | /// The type is the inferred error set of a specific function. | ||
| 2962 | error_set_inferred, | ||
| 2895 | empty_struct, | 2963 | empty_struct, |
| 2896 | @"opaque", | 2964 | @"opaque", |
| 2897 | @"struct", | 2965 | @"struct", |
| ... | @@ -2989,6 +3057,7 @@ pub const Type = extern union { | ... | @@ -2989,6 +3057,7 @@ pub const Type = extern union { |
| 2989 | => Payload.Bits, | 3057 | => Payload.Bits, |
| 2990 | 3058 | ||
| 2991 | .error_set => Payload.ErrorSet, | 3059 | .error_set => Payload.ErrorSet, |
| 3060 | .error_set_inferred => Payload.ErrorSetInferred, | ||
| 2992 | 3061 | ||
| 2993 | .array, .vector => Payload.Array, | 3062 | .array, .vector => Payload.Array, |
| 2994 | .array_sentinel => Payload.ArraySentinel, | 3063 | .array_sentinel => Payload.ArraySentinel, |
| ... | @@ -3081,6 +3150,16 @@ pub const Type = extern union { | ... | @@ -3081,6 +3150,16 @@ pub const Type = extern union { |
| 3081 | data: *Module.ErrorSet, | 3150 | data: *Module.ErrorSet, |
| 3082 | }; | 3151 | }; |
| 3083 | 3152 | ||
| 3153 | pub const ErrorSetInferred = struct { | ||
| 3154 | pub const base_tag = Tag.error_set_inferred; | ||
| 3155 | |||
| 3156 | base: Payload = Payload{ .tag = base_tag }, | ||
| 3157 | data: struct { | ||
| 3158 | func: *Module.Fn, | ||
| 3159 | map: std.StringHashMapUnmanaged(void), | ||
| 3160 | }, | ||
| 3161 | }; | ||
| 3162 | |||
| 3084 | pub const Pointer = struct { | 3163 | pub const Pointer = struct { |
| 3085 | pub const base_tag = Tag.pointer; | 3164 | pub const base_tag = Tag.pointer; |
| 3086 | 3165 |
src/value.zig+22-5| ... | @@ -483,13 +483,13 @@ pub const Value = extern union { | ... | @@ -483,13 +483,13 @@ pub const Value = extern union { |
| 483 | /// TODO this should become a debug dump() function. In order to print values in a meaningful way | 483 | /// TODO this should become a debug dump() function. In order to print values in a meaningful way |
| 484 | /// we also need access to the type. | 484 | /// we also need access to the type. |
| 485 | pub fn format( | 485 | pub fn format( |
| 486 | self: Value, | 486 | start_val: Value, |
| 487 | comptime fmt: []const u8, | 487 | comptime fmt: []const u8, |
| 488 | options: std.fmt.FormatOptions, | 488 | options: std.fmt.FormatOptions, |
| 489 | out_stream: anytype, | 489 | out_stream: anytype, |
| 490 | ) !void { | 490 | ) !void { |
| 491 | comptime assert(fmt.len == 0); | 491 | comptime assert(fmt.len == 0); |
| 492 | var val = self; | 492 | var val = start_val; |
| 493 | while (true) switch (val.tag()) { | 493 | while (true) switch (val.tag()) { |
| 494 | .u8_type => return out_stream.writeAll("u8"), | 494 | .u8_type => return out_stream.writeAll("u8"), |
| 495 | .i8_type => return out_stream.writeAll("i8"), | 495 | .i8_type => return out_stream.writeAll("i8"), |
| ... | @@ -598,9 +598,9 @@ pub const Value = extern union { | ... | @@ -598,9 +598,9 @@ pub const Value = extern union { |
| 598 | val = field_ptr.container_ptr; | 598 | val = field_ptr.container_ptr; |
| 599 | }, | 599 | }, |
| 600 | .empty_array => return out_stream.writeAll(".{}"), | 600 | .empty_array => return out_stream.writeAll(".{}"), |
| 601 | .enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(self.castTag(.enum_literal).?.data)}), | 601 | .enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(val.castTag(.enum_literal).?.data)}), |
| 602 | .enum_field_index => return out_stream.print("(enum field {d})", .{self.castTag(.enum_field_index).?.data}), | 602 | .enum_field_index => return out_stream.print("(enum field {d})", .{val.castTag(.enum_field_index).?.data}), |
| 603 | .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(self.castTag(.bytes).?.data)}), | 603 | .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}), |
| 604 | .repeated => { | 604 | .repeated => { |
| 605 | try out_stream.writeAll("(repeated) "); | 605 | try out_stream.writeAll("(repeated) "); |
| 606 | val = val.castTag(.repeated).?.data; | 606 | val = val.castTag(.repeated).?.data; |
| ... | @@ -1336,6 +1336,23 @@ pub const Value = extern union { | ... | @@ -1336,6 +1336,23 @@ pub const Value = extern union { |
| 1336 | }; | 1336 | }; |
| 1337 | } | 1337 | } |
| 1338 | 1338 | ||
| 1339 | pub fn sliceLen(val: Value) u64 { | ||
| 1340 | return switch (val.tag()) { | ||
| 1341 | .empty_array => 0, | ||
| 1342 | .bytes => val.castTag(.bytes).?.data.len, | ||
| 1343 | .ref_val => sliceLen(val.castTag(.ref_val).?.data), | ||
| 1344 | .decl_ref => { | ||
| 1345 | const decl = val.castTag(.decl_ref).?.data; | ||
| 1346 | if (decl.ty.zigTypeTag() == .Array) { | ||
| 1347 | return decl.ty.arrayLen(); | ||
| 1348 | } else { | ||
| 1349 | return 1; | ||
| 1350 | } | ||
| 1351 | }, | ||
| 1352 | else => unreachable, | ||
| 1353 | }; | ||
| 1354 | } | ||
| 1355 | |||
| 1339 | /// Asserts the value is a single-item pointer to an array, or an array, | 1356 | /// Asserts the value is a single-item pointer to an array, or an array, |
| 1340 | /// or an unknown-length pointer, and returns the element value at the index. | 1357 | /// or an unknown-length pointer, and returns the element value at the index. |
| 1341 | pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value { | 1358 | pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value { |
test/stage2/cbe.zig+20-13| ... | @@ -804,19 +804,26 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -804,19 +804,26 @@ pub fn addCases(ctx: *TestContext) !void { |
| 804 | }); | 804 | }); |
| 805 | } | 805 | } |
| 806 | 806 | ||
| 807 | ctx.c("empty start function", linux_x64, | 807 | { |
| 808 | \\export fn _start() noreturn { | 808 | var case = ctx.exeFromCompiledC("inferred error sets", .{}); |
| 809 | \\ unreachable; | 809 | |
| 810 | \\} | 810 | case.addCompareOutput( |
| 811 | , | 811 | \\pub export fn main() c_int { |
| 812 | \\ZIG_EXTERN_C zig_noreturn void _start(void); | 812 | \\ if (foo()) |_| { |
| 813 | \\ | 813 | \\ @panic("test fail"); |
| 814 | \\zig_noreturn void _start(void) { | 814 | \\ } else |err| { |
| 815 | \\ zig_breakpoint(); | 815 | \\ if (err != error.ItBroke) { |
| 816 | \\ zig_unreachable(); | 816 | \\ @panic("test fail"); |
| 817 | \\} | 817 | \\ } |
| 818 | \\ | 818 | \\ } |
| 819 | ); | 819 | \\ return 0; |
| 820 | \\} | ||
| 821 | \\fn foo() !void { | ||
| 822 | \\ return error.ItBroke; | ||
| 823 | \\} | ||
| 824 | , ""); | ||
| 825 | } | ||
| 826 | |||
| 820 | ctx.h("simple header", linux_x64, | 827 | ctx.h("simple header", linux_x64, |
| 821 | \\export fn start() void{} | 828 | \\export fn start() void{} |
| 822 | , | 829 | , |
test/stage2/wasm.zig-2| ... | @@ -587,8 +587,6 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -587,8 +587,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 587 | } | 587 | } |
| 588 | 588 | ||
| 589 | { | 589 | { |
| 590 | // TODO implement Type equality comparison of error unions in SEMA | ||
| 591 | // before we can incrementally compile functions with an error union as return type | ||
| 592 | var case = ctx.exe("wasm error union part 2", wasi); | 590 | var case = ctx.exe("wasm error union part 2", wasi); |
| 593 | 591 | ||
| 594 | case.addCompareOutput( | 592 | case.addCompareOutput( |