| author | |
| committer | |
| log | 62d27fcfb687e3ab1f10c72513e19529d8ffceed |
| tree | 2885da99ca325959e40f417346aca63ddef4fb31 |
| parent | 7935e83b1d5d29cca058597ebdac6dfd012a790a |
| parent | c2e66d9bab396a69514ec7c3c41fb0404e542f21 |
| signature |
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 | 677 | /// therefore must be kept in sync with the compiler implementation. |
| 678 | 678 | pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn { |
| 679 | 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 | 687 | if (@hasDecl(root, "os") and @hasDecl(root.os, "panic")) { |
| 681 | 688 | root.os.panic(msg, error_return_trace); |
| 682 | 689 | unreachable; |
lib/std/hash_map.zig+26-6| ... | ... | @@ -483,10 +483,20 @@ pub fn HashMap( |
| 483 | 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 | 489 | /// Increases capacity, guaranteeing that insertions up until the |
| 487 | 490 | /// `expected_count` will not cause an allocation, and therefore cannot fail. |
| 488 | pub fn ensureCapacity(self: *Self, expected_count: Size) !void { | |
| 489 | return self.unmanaged.ensureCapacityContext(self.allocator, expected_count, self.ctx); | |
| 491 | pub fn ensureTotalCapacity(self: *Self, expected_count: Size) !void { | |
| 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 | 502 | /// Returns the number of total elements which may be present before it is |
| ... | ... | @@ -821,16 +831,26 @@ pub fn HashMapUnmanaged( |
| 821 | 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 | 838 | if (@sizeOf(Context) != 0) |
| 826 | @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call ensureCapacityContext instead."); | |
| 827 | return ensureCapacityContext(self, allocator, new_size, undefined); | |
| 839 | @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call ensureTotalCapacityContext instead."); | |
| 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 | 843 | if (new_size > self.size) |
| 831 | 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 | 854 | pub fn clearRetainingCapacity(self: *Self) void { |
| 835 | 855 | if (self.metadata) |_| { |
| 836 | 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 | 786 | rl, |
| 787 | 787 | node, |
| 788 | 788 | node_datas[node].lhs, |
| 789 | .is_err_ptr, | |
| 789 | .is_non_err_ptr, | |
| 790 | 790 | .err_union_payload_unsafe_ptr, |
| 791 | 791 | .err_union_code_ptr, |
| 792 | 792 | node_datas[node].rhs, |
| ... | ... | @@ -798,7 +798,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr |
| 798 | 798 | rl, |
| 799 | 799 | node, |
| 800 | 800 | node_datas[node].lhs, |
| 801 | .is_err, | |
| 801 | .is_non_err, | |
| 802 | 802 | .err_union_payload_unsafe, |
| 803 | 803 | .err_union_code, |
| 804 | 804 | node_datas[node].rhs, |
| ... | ... | @@ -813,7 +813,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr |
| 813 | 813 | rl, |
| 814 | 814 | node, |
| 815 | 815 | node_datas[node].lhs, |
| 816 | .is_null_ptr, | |
| 816 | .is_non_null_ptr, | |
| 817 | 817 | .optional_payload_unsafe_ptr, |
| 818 | 818 | undefined, |
| 819 | 819 | node_datas[node].rhs, |
| ... | ... | @@ -825,7 +825,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr |
| 825 | 825 | rl, |
| 826 | 826 | node, |
| 827 | 827 | node_datas[node].lhs, |
| 828 | .is_null, | |
| 828 | .is_non_null, | |
| 829 | 829 | .optional_payload_unsafe, |
| 830 | 830 | undefined, |
| 831 | 831 | node_datas[node].rhs, |
| ... | ... | @@ -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 | 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 | 1948 | .float128, |
| 1949 | 1949 | .int_type, |
| 1950 | 1950 | .is_non_null, |
| 1951 | .is_null, | |
| 1952 | 1951 | .is_non_null_ptr, |
| 1953 | .is_null_ptr, | |
| 1954 | .is_err, | |
| 1955 | .is_err_ptr, | |
| 1952 | .is_non_err, | |
| 1953 | .is_non_err_ptr, | |
| 1956 | 1954 | .mod_rem, |
| 1957 | 1955 | .mul, |
| 1958 | 1956 | .mulwrap, |
| ... | ... | @@ -2102,6 +2100,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2102 | 2100 | .@"resume", |
| 2103 | 2101 | .@"await", |
| 2104 | 2102 | .await_nosuspend, |
| 2103 | .ret_err_value_code, | |
| 2105 | 2104 | .extended, |
| 2106 | 2105 | => break :b false, |
| 2107 | 2106 | |
| ... | ... | @@ -2113,6 +2112,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2113 | 2112 | .compile_error, |
| 2114 | 2113 | .ret_node, |
| 2115 | 2114 | .ret_coerce, |
| 2115 | .ret_err_value, | |
| 2116 | 2116 | .@"unreachable", |
| 2117 | 2117 | .repeat, |
| 2118 | 2118 | .repeat_inline, |
| ... | ... | @@ -2162,13 +2162,63 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2162 | 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 | 2216 | fn genDefers( |
| 2166 | 2217 | gz: *GenZir, |
| 2167 | 2218 | outer_scope: *Scope, |
| 2168 | 2219 | inner_scope: *Scope, |
| 2169 | err_code: Zir.Inst.Ref, | |
| 2220 | which_ones: DefersToEmit, | |
| 2170 | 2221 | ) InnerError!void { |
| 2171 | _ = err_code; | |
| 2172 | 2222 | const astgen = gz.astgen; |
| 2173 | 2223 | const tree = astgen.tree; |
| 2174 | 2224 | const node_datas = tree.nodes.items(.data); |
| ... | ... | @@ -2191,12 +2241,37 @@ fn genDefers( |
| 2191 | 2241 | .defer_error => { |
| 2192 | 2242 | const defer_scope = scope.cast(Scope.Defer).?; |
| 2193 | 2243 | scope = defer_scope.parent; |
| 2194 | if (err_code == .none) continue; | |
| 2195 | const expr_node = node_datas[defer_scope.defer_node].rhs; | |
| 2196 | const prev_in_defer = gz.in_defer; | |
| 2197 | gz.in_defer = true; | |
| 2198 | defer gz.in_defer = prev_in_defer; | |
| 2199 | _ = try unusedResultExpr(gz, defer_scope.parent, expr_node); | |
| 2244 | switch (which_ones) { | |
| 2245 | .both_sans_err => { | |
| 2246 | const expr_node = node_datas[defer_scope.defer_node].rhs; | |
| 2247 | const prev_in_defer = gz.in_defer; | |
| 2248 | gz.in_defer = true; | |
| 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 | 2276 | .namespace => unreachable, |
| 2202 | 2277 | .top => unreachable, |
| ... | ... | @@ -4544,8 +4619,8 @@ fn tryExpr( |
| 4544 | 4619 | }; |
| 4545 | 4620 | const err_ops = switch (rl) { |
| 4546 | 4621 | // zig fmt: off |
| 4547 | .ref => [3]Zir.Inst.Tag{ .is_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 }, | |
| 4622 | .ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr }, | |
| 4623 | else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe }, | |
| 4549 | 4624 | // zig fmt: on |
| 4550 | 4625 | }; |
| 4551 | 4626 | // This could be a pointer or value depending on the `operand_rl` parameter. |
| ... | ... | @@ -4563,21 +4638,21 @@ fn tryExpr( |
| 4563 | 4638 | var then_scope = parent_gz.makeSubBlock(scope); |
| 4564 | 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 | 4641 | block_scope.break_count += 1; |
| 4574 | 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); | |
| 4576 | const else_result = switch (rl) { | |
| 4643 | const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node); | |
| 4644 | const then_result = switch (rl) { | |
| 4577 | 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 | 4656 | return finishThenElseBlock( |
| 4582 | 4657 | parent_gz, |
| 4583 | 4658 | rl, |
| ... | ... | @@ -4634,18 +4709,28 @@ fn orelseCatchExpr( |
| 4634 | 4709 | var then_scope = parent_gz.makeSubBlock(scope); |
| 4635 | 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 | 4722 | var err_val_scope: Scope.LocalVal = undefined; |
| 4638 | const then_sub_scope = blk: { | |
| 4639 | const payload = payload_token orelse break :blk &then_scope.base; | |
| 4723 | const else_sub_scope = blk: { | |
| 4724 | const payload = payload_token orelse break :blk &else_scope.base; | |
| 4640 | 4725 | if (mem.eql(u8, tree.tokenSlice(payload), "_")) { |
| 4641 | 4726 | return astgen.failTok(payload, "discard of error capture; omit it instead", .{}); |
| 4642 | 4727 | } |
| 4643 | 4728 | const err_name = try astgen.identAsString(payload); |
| 4644 | 4729 | err_val_scope = .{ |
| 4645 | .parent = &then_scope.base, | |
| 4646 | .gen_zir = &then_scope, | |
| 4730 | .parent = &else_scope.base, | |
| 4731 | .gen_zir = &else_scope, | |
| 4647 | 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 | 4734 | .token_src = payload, |
| 4650 | 4735 | .id_cat = .@"capture", |
| 4651 | 4736 | }; |
| ... | ... | @@ -4653,23 +4738,13 @@ fn orelseCatchExpr( |
| 4653 | 4738 | }; |
| 4654 | 4739 | |
| 4655 | 4740 | block_scope.break_count += 1; |
| 4656 | const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, rhs); | |
| 4657 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); | |
| 4741 | const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs); | |
| 4742 | try checkUsed(parent_gz, &else_scope.base, else_sub_scope); | |
| 4658 | 4743 | |
| 4659 | 4744 | // We hold off on the break instructions as well as copying the then/else |
| 4660 | 4745 | // instructions into place until we know whether to keep store_to_block_ptr |
| 4661 | 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 | 4748 | return finishThenElseBlock( |
| 4674 | 4749 | parent_gz, |
| 4675 | 4750 | rl, |
| ... | ... | @@ -4887,7 +4962,7 @@ fn ifExpr( |
| 4887 | 4962 | if (if_full.error_token) |_| { |
| 4888 | 4963 | const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none; |
| 4889 | 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 | 4966 | break :c .{ |
| 4892 | 4967 | .inst = err_union, |
| 4893 | 4968 | .bool_bit = try block_scope.addUnNode(tag, err_union, node), |
| ... | ... | @@ -5144,7 +5219,7 @@ fn whileExpr( |
| 5144 | 5219 | if (while_full.error_token) |_| { |
| 5145 | 5220 | const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none; |
| 5146 | 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 | 5223 | break :c .{ |
| 5149 | 5224 | .inst = err_union, |
| 5150 | 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 | 6165 | const astgen = gz.astgen; |
| 6091 | 6166 | const tree = astgen.tree; |
| 6092 | 6167 | const node_datas = tree.nodes.items(.data); |
| 6168 | const node_tags = tree.nodes.items(.tag); | |
| 6093 | 6169 | |
| 6094 | 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 | 6174 | const operand_node = node_datas[node].lhs; |
| 6097 | 6175 | if (operand_node == 0) { |
| 6098 | 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 | 6178 | _ = try gz.addUnNode(.ret_node, .void_value, node); |
| 6101 | 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 | 6199 | const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{ |
| 6105 | 6200 | .ptr = try gz.addNodeExtended(.ret_ptr, node), |
| 6106 | 6201 | } else .{ |
| ... | ... | @@ -6111,34 +6206,46 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6111 | 6206 | switch (nodeMayEvalToError(tree, operand_node)) { |
| 6112 | 6207 | .never => { |
| 6113 | 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 | 6210 | _ = try gz.addUnNode(.ret_node, operand, node); |
| 6116 | 6211 | return Zir.Inst.Ref.unreachable_value; |
| 6117 | 6212 | }, |
| 6118 | 6213 | .always => { |
| 6119 | 6214 | // Value is always an error. Emit both error defers and regular defers. |
| 6120 | 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 | 6217 | _ = try gz.addUnNode(.ret_node, operand, node); |
| 6123 | 6218 | return Zir.Inst.Ref.unreachable_value; |
| 6124 | 6219 | }, |
| 6125 | 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 | 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 | 6231 | const condbr = try gz.addCondBr(.condbr, node); |
| 6129 | 6232 | |
| 6130 | 6233 | var then_scope = gz.makeSubBlock(scope); |
| 6131 | 6234 | defer then_scope.instructions.deinit(astgen.gpa); |
| 6132 | const err_code = try then_scope.addUnNode(.err_union_code, operand, node); | |
| 6133 | try genDefers(&then_scope, &astgen.fn_block.?.base, scope, err_code); | |
| 6235 | ||
| 6236 | try genDefers(&then_scope, defer_outer, scope, .normal_only); | |
| 6134 | 6237 | _ = try then_scope.addUnNode(.ret_node, operand, node); |
| 6135 | 6238 | |
| 6136 | 6239 | var else_scope = gz.makeSubBlock(scope); |
| 6137 | 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 | 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 | 6250 | return Zir.Inst.Ref.unreachable_value; |
| 6144 | 6251 | }, |
| ... | ... | @@ -6885,7 +6992,7 @@ fn builtinCall( |
| 6885 | 6992 | .field => { |
| 6886 | 6993 | const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); |
| 6887 | 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 | 6996 | .lhs = try expr(gz, scope, .ref, params[0]), |
| 6890 | 6997 | .field_name = field_name, |
| 6891 | 6998 | }); |
src/Module.zig+17-2| ... | ... | @@ -755,6 +755,7 @@ pub const Fn = struct { |
| 755 | 755 | rbrace_column: u16, |
| 756 | 756 | |
| 757 | 757 | state: Analysis, |
| 758 | is_cold: bool = false, | |
| 758 | 759 | |
| 759 | 760 | pub const Analysis = enum { |
| 760 | 761 | queued, |
| ... | ... | @@ -776,8 +777,19 @@ pub const Fn = struct { |
| 776 | 777 | } |
| 777 | 778 | |
| 778 | 779 | pub fn deinit(func: *Fn, gpa: *Allocator) void { |
| 779 | _ = func; | |
| 780 | _ = gpa; | |
| 780 | if (func.getInferredErrorSet()) |map| { | |
| 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 | 3465 | for (decl.dependencies.keys()) |dep| { |
| 3454 | 3466 | dep.removeDependant(decl); |
| 3455 | 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 | 3471 | // We don't recursively perform a deletion here, because during the update, |
| 3457 | 3472 | // another reference to it may turn up. |
| 3458 | 3473 | dep.deletion_flag = true; |
src/Sema.zig+224-88| ... | ... | @@ -225,12 +225,10 @@ pub fn analyzeBody( |
| 225 | 225 | .float => try sema.zirFloat(block, inst), |
| 226 | 226 | .float128 => try sema.zirFloat128(block, inst), |
| 227 | 227 | .int_type => try sema.zirIntType(block, inst), |
| 228 | .is_err => try sema.zirIsErr(block, inst), | |
| 229 | .is_err_ptr => try sema.zirIsErrPtr(block, inst), | |
| 230 | .is_non_null => try sema.zirIsNull(block, inst, true), | |
| 231 | .is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true), | |
| 232 | .is_null => try sema.zirIsNull(block, inst, false), | |
| 233 | .is_null_ptr => try sema.zirIsNullPtr(block, inst, false), | |
| 228 | .is_non_err => try sema.zirIsNonErr(block, inst), | |
| 229 | .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), | |
| 230 | .is_non_null => try sema.zirIsNonNull(block, inst), | |
| 231 | .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), | |
| 234 | 232 | .loop => try sema.zirLoop(block, inst), |
| 235 | 233 | .merge_error_sets => try sema.zirMergeErrorSets(block, inst), |
| 236 | 234 | .negate => try sema.zirNegate(block, inst, .sub), |
| ... | ... | @@ -244,6 +242,7 @@ pub fn analyzeBody( |
| 244 | 242 | .ptr_type => try sema.zirPtrType(block, inst), |
| 245 | 243 | .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst), |
| 246 | 244 | .ref => try sema.zirRef(block, inst), |
| 245 | .ret_err_value_code => try sema.zirRetErrValueCode(block, inst), | |
| 247 | 246 | .shl => try sema.zirShl(block, inst), |
| 248 | 247 | .shr => try sema.zirShr(block, inst), |
| 249 | 248 | .slice_end => try sema.zirSliceEnd(block, inst), |
| ... | ... | @@ -380,8 +379,9 @@ pub fn analyzeBody( |
| 380 | 379 | .condbr => return sema.zirCondbr(block, inst), |
| 381 | 380 | .@"break" => return sema.zirBreak(block, inst), |
| 382 | 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 | 383 | .ret_node => return sema.zirRetNode(block, inst), |
| 384 | .ret_err_value => return sema.zirRetErrValue(block, inst), | |
| 385 | 385 | .@"unreachable" => return sema.zirUnreachable(block, inst), |
| 386 | 386 | .repeat => return sema.zirRepeat(block, inst), |
| 387 | 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 | 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 | 603 | fn resolveConstString( |
| 591 | 604 | sema: *Sema, |
| 592 | 605 | block: *Scope.Block, |
| ... | ... | @@ -1754,8 +1767,9 @@ fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError! |
| 1754 | 1767 | fn zirPanic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index { |
| 1755 | 1768 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 1756 | 1769 | const src: LazySrcLoc = inst_data.src(); |
| 1757 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirPanic", .{}); | |
| 1758 | //return always_noreturn; | |
| 1770 | const msg_inst = try sema.resolveInst(inst_data.operand); | |
| 1771 | ||
| 1772 | return sema.panicWithMsg(block, src, msg_inst); | |
| 1759 | 1773 | } |
| 1760 | 1774 | |
| 1761 | 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 | 2042 | |
| 2029 | 2043 | fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| 2030 | 2044 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 2031 | const src: LazySrcLoc = inst_data.src(); | |
| 2032 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetCold", .{}); | |
| 2045 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 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 | 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 | 2057 | fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| 2042 | 2058 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 2043 | 2059 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 2044 | ||
| 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; | |
| 2060 | block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand); | |
| 2049 | 2061 | } |
| 2050 | 2062 | |
| 2051 | 2063 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| ... | ... | @@ -2190,21 +2202,27 @@ fn zirCall( |
| 2190 | 2202 | const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index); |
| 2191 | 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 | 2216 | fn analyzeCall( |
| 2197 | 2217 | sema: *Sema, |
| 2198 | 2218 | block: *Scope.Block, |
| 2199 | zir_func: Zir.Inst.Ref, | |
| 2219 | func: *ir.Inst, | |
| 2200 | 2220 | func_src: LazySrcLoc, |
| 2201 | 2221 | call_src: LazySrcLoc, |
| 2202 | 2222 | modifier: std.builtin.CallOptions.Modifier, |
| 2203 | 2223 | ensure_result_used: bool, |
| 2204 | zir_args: []const Zir.Inst.Ref, | |
| 2224 | args: []const *ir.Inst, | |
| 2205 | 2225 | ) InnerError!*ir.Inst { |
| 2206 | const func = try sema.resolveInst(zir_func); | |
| 2207 | ||
| 2208 | 2226 | if (func.ty.zigTypeTag() != .Fn) |
| 2209 | 2227 | return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty}); |
| 2210 | 2228 | |
| ... | ... | @@ -2221,22 +2239,22 @@ fn analyzeCall( |
| 2221 | 2239 | const fn_params_len = func.ty.fnParamLen(); |
| 2222 | 2240 | if (func.ty.fnIsVarArgs()) { |
| 2223 | 2241 | assert(cc == .C); |
| 2224 | if (zir_args.len < fn_params_len) { | |
| 2242 | if (args.len < fn_params_len) { | |
| 2225 | 2243 | // TODO add error note: declared here |
| 2226 | 2244 | return sema.mod.fail( |
| 2227 | 2245 | &block.base, |
| 2228 | 2246 | func_src, |
| 2229 | 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 | 2252 | // TODO add error note: declared here |
| 2235 | 2253 | return sema.mod.fail( |
| 2236 | 2254 | &block.base, |
| 2237 | 2255 | func_src, |
| 2238 | 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 | 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 | 2277 | const ret_type = func.ty.fnReturnType(); |
| 2267 | 2278 | |
| 2268 | 2279 | const is_comptime_call = block.is_comptime or modifier == .compile_time; |
| ... | ... | @@ -2323,7 +2334,7 @@ fn analyzeCall( |
| 2323 | 2334 | defer sema.func = parent_func; |
| 2324 | 2335 | |
| 2325 | 2336 | const parent_param_inst_list = sema.param_inst_list; |
| 2326 | sema.param_inst_list = casted_args; | |
| 2337 | sema.param_inst_list = args; | |
| 2327 | 2338 | defer sema.param_inst_list = parent_param_inst_list; |
| 2328 | 2339 | |
| 2329 | 2340 | const parent_next_arg_index = sema.next_arg_index; |
| ... | ... | @@ -2357,7 +2368,7 @@ fn analyzeCall( |
| 2357 | 2368 | break :res result; |
| 2358 | 2369 | } else res: { |
| 2359 | 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 | 2374 | if (ensure_result_used) { |
| ... | ... | @@ -2968,17 +2979,19 @@ fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner |
| 2968 | 2979 | if (operand.ty.zigTypeTag() != .ErrorUnion) |
| 2969 | 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 | 2984 | if (operand.value()) |val| { |
| 2972 | 2985 | assert(val.getError() != null); |
| 2973 | 2986 | const data = val.castTag(.error_union).?.data; |
| 2974 | 2987 | return sema.mod.constInst(sema.arena, src, .{ |
| 2975 | .ty = operand.ty.castTag(.error_union).?.data.error_set, | |
| 2988 | .ty = result_ty, | |
| 2976 | 2989 | .val = data, |
| 2977 | 2990 | }); |
| 2978 | 2991 | } |
| 2979 | 2992 | |
| 2980 | 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 | 2997 | /// Pointer in, value out |
| ... | ... | @@ -2994,18 +3007,20 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In |
| 2994 | 3007 | if (operand.ty.elemType().zigTypeTag() != .ErrorUnion) |
| 2995 | 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 | 3012 | if (operand.value()) |pointer_val| { |
| 2998 | 3013 | const val = try pointer_val.pointerDeref(sema.arena); |
| 2999 | 3014 | assert(val.getError() != null); |
| 3000 | 3015 | const data = val.castTag(.error_union).?.data; |
| 3001 | 3016 | return sema.mod.constInst(sema.arena, src, .{ |
| 3002 | .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set, | |
| 3017 | .ty = result_ty, | |
| 3003 | 3018 | .val = data, |
| 3004 | 3019 | }); |
| 3005 | 3020 | } |
| 3006 | 3021 | |
| 3007 | 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 | 3026 | fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| ... | ... | @@ -3081,28 +3096,31 @@ fn funcCommon( |
| 3081 | 3096 | ) InnerError!*Inst { |
| 3082 | 3097 | const src: LazySrcLoc = .{ .node_offset = src_node_offset }; |
| 3083 | 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 | 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 | 3106 | const fn_ty: Type = fn_ty: { |
| 3089 | 3107 | // Hot path for some common function types. |
| 3090 | 3108 | if (zir_param_types.len == 0 and !var_args and align_val.tag() == .null_value and |
| 3091 | 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 | 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 | 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 | 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 | 3124 | break :fn_ty Type.initTag(.fn_ccc_void_no_args); |
| 3107 | 3125 | } |
| 3108 | 3126 | } |
| ... | ... | @@ -3120,9 +3138,16 @@ fn funcCommon( |
| 3120 | 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) { | |
| 3124 | return mod.fail(&block.base, src, "TODO implement functions with inferred error sets", .{}); | |
| 3125 | } | |
| 3141 | const return_type = if (!inferred_error_set) bare_return_type else blk: { | |
| 3142 | const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, .{ | |
| 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 | 3152 | break :fn_ty try Type.Tag.function.create(sema.arena, .{ |
| 3128 | 3153 | .param_types = param_types, |
| ... | ... | @@ -3188,7 +3213,6 @@ fn funcCommon( |
| 3188 | 3213 | const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued; |
| 3189 | 3214 | |
| 3190 | 3215 | const fn_payload = try sema.arena.create(Value.Payload.Function); |
| 3191 | const new_func = try sema.gpa.create(Module.Fn); | |
| 3192 | 3216 | new_func.* = .{ |
| 3193 | 3217 | .state = anal_state, |
| 3194 | 3218 | .zir_body_inst = body_inst, |
| ... | ... | @@ -4542,6 +4566,12 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError! |
| 4542 | 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 | 4575 | fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { |
| 4546 | 4576 | const tracy = trace(@src()); |
| 4547 | 4577 | defer tracy.end(); |
| ... | ... | @@ -5273,11 +5303,10 @@ fn zirBoolBr( |
| 5273 | 5303 | return &block_inst.base; |
| 5274 | 5304 | } |
| 5275 | 5305 | |
| 5276 | fn zirIsNull( | |
| 5306 | fn zirIsNonNull( | |
| 5277 | 5307 | sema: *Sema, |
| 5278 | 5308 | block: *Scope.Block, |
| 5279 | 5309 | inst: Zir.Inst.Index, |
| 5280 | invert_logic: bool, | |
| 5281 | 5310 | ) InnerError!*Inst { |
| 5282 | 5311 | const tracy = trace(@src()); |
| 5283 | 5312 | defer tracy.end(); |
| ... | ... | @@ -5285,14 +5314,13 @@ fn zirIsNull( |
| 5285 | 5314 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 5286 | 5315 | const src = inst_data.src(); |
| 5287 | 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 | 5321 | sema: *Sema, |
| 5293 | 5322 | block: *Scope.Block, |
| 5294 | 5323 | inst: Zir.Inst.Index, |
| 5295 | invert_logic: bool, | |
| 5296 | 5324 | ) InnerError!*Inst { |
| 5297 | 5325 | const tracy = trace(@src()); |
| 5298 | 5326 | defer tracy.end(); |
| ... | ... | @@ -5301,19 +5329,19 @@ fn zirIsNullPtr( |
| 5301 | 5329 | const src = inst_data.src(); |
| 5302 | 5330 | const ptr = try sema.resolveInst(inst_data.operand); |
| 5303 | 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 | 5336 | const tracy = trace(@src()); |
| 5309 | 5337 | defer tracy.end(); |
| 5310 | 5338 | |
| 5311 | 5339 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 5312 | 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 | 5345 | const tracy = trace(@src()); |
| 5318 | 5346 | defer tracy.end(); |
| 5319 | 5347 | |
| ... | ... | @@ -5321,7 +5349,7 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro |
| 5321 | 5349 | const src = inst_data.src(); |
| 5322 | 5350 | const ptr = try sema.resolveInst(inst_data.operand); |
| 5323 | 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 | 5355 | fn zirCondbr( |
| ... | ... | @@ -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 | 5444 | sema: *Sema, |
| 5393 | 5445 | block: *Scope.Block, |
| 5394 | 5446 | inst: Zir.Inst.Index, |
| ... | ... | @@ -6195,6 +6247,10 @@ fn zirFuncExtended( |
| 6195 | 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 | 6254 | return sema.funcCommon( |
| 6199 | 6255 | block, |
| 6200 | 6256 | extra.data.src_node, |
| ... | ... | @@ -6203,9 +6259,9 @@ fn zirFuncExtended( |
| 6203 | 6259 | extra.data.return_type, |
| 6204 | 6260 | cc, |
| 6205 | 6261 | align_val, |
| 6206 | small.is_var_args, | |
| 6207 | small.is_inferred_error, | |
| 6208 | small.is_extern, | |
| 6262 | is_var_args, | |
| 6263 | is_inferred_error, | |
| 6264 | is_extern, | |
| 6209 | 6265 | src_locs, |
| 6210 | 6266 | lib_name, |
| 6211 | 6267 | ); |
| ... | ... | @@ -6357,15 +6413,75 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: |
| 6357 | 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 { | |
| 6361 | _ = sema; | |
| 6362 | _ = panic_id; | |
| 6363 | // TODO Once we have a panic function to call, call it here instead of breakpoint. | |
| 6364 | _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint); | |
| 6365 | _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach); | |
| 6416 | fn panicWithMsg( | |
| 6417 | sema: *Sema, | |
| 6418 | block: *Scope.Block, | |
| 6419 | src: LazySrcLoc, | |
| 6420 | msg_inst: *ir.Inst, | |
| 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 | 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 | 6485 | fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { |
| 6370 | 6486 | sema.branch_count += 1; |
| 6371 | 6487 | if (sema.branch_count > sema.branch_quota) { |
| ... | ... | @@ -7102,20 +7218,25 @@ fn analyzeIsNull( |
| 7102 | 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 | 7227 | const ot = operand.ty.zigTypeTag(); |
| 7107 | if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false); | |
| 7108 | if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true); | |
| 7228 | if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, true); | |
| 7229 | if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, false); | |
| 7109 | 7230 | assert(ot == .ErrorUnion); |
| 7110 | 7231 | const result_ty = Type.initTag(.bool); |
| 7111 | 7232 | if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| { |
| 7112 | 7233 | if (err_union.isUndef()) { |
| 7113 | 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 | 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 | 7242 | fn analyzeSlice( |
| ... | ... | @@ -7377,15 +7498,13 @@ fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) |
| 7377 | 7498 | } |
| 7378 | 7499 | |
| 7379 | 7500 | fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { |
| 7380 | // TODO deal with inferred error sets | |
| 7381 | 7501 | const err_union = dest_type.castTag(.error_union).?; |
| 7382 | 7502 | if (inst.value()) |val| { |
| 7383 | const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: { | |
| 7503 | if (inst.ty.zigTypeTag() != .ErrorSet) { | |
| 7384 | 7504 | _ = try sema.coerce(block, err_union.data.payload, inst, inst.src); |
| 7385 | break :blk val; | |
| 7386 | 7505 | } else switch (err_union.data.error_set.tag()) { |
| 7387 | .anyerror => val, | |
| 7388 | .error_set_single => blk: { | |
| 7506 | .anyerror => {}, | |
| 7507 | .error_set_single => { | |
| 7389 | 7508 | const expected_name = val.castTag(.@"error").?.data.name; |
| 7390 | 7509 | const n = err_union.data.error_set.castTag(.error_set_single).?.data; |
| 7391 | 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 | 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 | 7520 | const expected_name = val.castTag(.@"error").?.data.name; |
| 7403 | 7521 | const error_set = err_union.data.error_set.castTag(.error_set).?.data; |
| 7404 | 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 | 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 | 7549 | else => unreachable, |
| 7421 | }; | |
| 7550 | } | |
| 7422 | 7551 | |
| 7423 | 7552 | return sema.mod.constInst(sema.arena, inst.src, .{ |
| 7424 | 7553 | .ty = dest_type, |
| 7425 | 7554 | // creating a SubValue for the error_union payload |
| 7426 | .val = try Value.Tag.error_union.create( | |
| 7427 | sema.arena, | |
| 7428 | to_wrap, | |
| 7429 | ), | |
| 7555 | .val = try Value.Tag.error_union.create(sema.arena, val), | |
| 7430 | 7556 | }); |
| 7431 | 7557 | } |
| 7432 | 7558 | |
| ... | ... | @@ -7573,12 +7699,12 @@ fn resolveBuiltinTypeFields( |
| 7573 | 7699 | return sema.resolveTypeFields(block, src, resolved_ty); |
| 7574 | 7700 | } |
| 7575 | 7701 | |
| 7576 | fn getBuiltinType( | |
| 7702 | fn getBuiltin( | |
| 7577 | 7703 | sema: *Sema, |
| 7578 | 7704 | block: *Scope.Block, |
| 7579 | 7705 | src: LazySrcLoc, |
| 7580 | 7706 | name: []const u8, |
| 7581 | ) InnerError!Type { | |
| 7707 | ) InnerError!*ir.Inst { | |
| 7582 | 7708 | const mod = sema.mod; |
| 7583 | 7709 | const std_pkg = mod.root_pkg.table.get("std").?; |
| 7584 | 7710 | const std_file = (mod.importPkg(std_pkg) catch unreachable).file; |
| ... | ... | @@ -7596,7 +7722,16 @@ fn getBuiltinType( |
| 7596 | 7722 | builtin_ty.getNamespace().?, |
| 7597 | 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 | 7735 | return sema.resolveAirAsType(block, src, ty_inst); |
| 7601 | 7736 | } |
| 7602 | 7737 | |
| ... | ... | @@ -7662,6 +7797,7 @@ fn typeHasOnePossibleValue( |
| 7662 | 7797 | .error_union, |
| 7663 | 7798 | .error_set, |
| 7664 | 7799 | .error_set_single, |
| 7800 | .error_set_inferred, | |
| 7665 | 7801 | .@"opaque", |
| 7666 | 7802 | .var_args_param, |
| 7667 | 7803 | .manyptr_u8, |
src/Zir.zig+33-26| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 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 | 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 | 5 | //! machine code, without any memory access into the AST tree token list, node list, |
| 6 | 6 | //! or source bytes. Exceptions include: |
| 7 | 7 | //! * Compile errors, which may need to reach into these data structures to |
| ... | ... | @@ -398,26 +398,20 @@ pub const Inst = struct { |
| 398 | 398 | /// Return a boolean false if an optional is null. `x != null` |
| 399 | 399 | /// Uses the `un_node` field. |
| 400 | 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 | 401 | /// Return a boolean false if an optional is null. `x.* != null` |
| 405 | 402 | /// Uses the `un_node` field. |
| 406 | 403 | is_non_null_ptr, |
| 407 | /// Return a boolean true if an optional is null. `x.* == null` | |
| 408 | /// Uses the `un_node` field. | |
| 409 | is_null_ptr, | |
| 410 | /// Return a boolean true if value is an error | |
| 404 | /// Return a boolean false if value is an error | |
| 411 | 405 | /// Uses the `un_node` field. |
| 412 | is_err, | |
| 413 | /// Return a boolean true if dereferenced pointer is an error | |
| 406 | is_non_err, | |
| 407 | /// Return a boolean false if dereferenced pointer is an error | |
| 414 | 408 | /// Uses the `un_node` field. |
| 415 | is_err_ptr, | |
| 409 | is_non_err_ptr, | |
| 416 | 410 | /// A labeled block of code that loops forever. At the end of the body will have either |
| 417 | 411 | /// a `repeat` instruction or a `repeat_inline` instruction. |
| 418 | 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 | |
| 420 | /// needs to emit more than 1 TZIR block for this instruction. | |
| 413 | /// This ZIR instruction is needed because AIR does not (yet?) match ZIR, and Sema | |
| 414 | /// needs to emit more than 1 AIR block for this instruction. | |
| 421 | 415 | /// The payload is `Block`. |
| 422 | 416 | loop, |
| 423 | 417 | /// Sends runtime control flow back to the beginning of the current block. |
| ... | ... | @@ -466,6 +460,19 @@ pub const Inst = struct { |
| 466 | 460 | /// Uses the `un_tok` union field. |
| 467 | 461 | /// The operand needs to get coerced to the function's return type. |
| 468 | 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 | 476 | /// Create a pointer type that does not have a sentinel, alignment, or bit range specified. |
| 470 | 477 | /// Uses the `ptr_type_simple` union field. |
| 471 | 478 | ptr_type_simple, |
| ... | ... | @@ -1033,11 +1040,9 @@ pub const Inst = struct { |
| 1033 | 1040 | .float128, |
| 1034 | 1041 | .int_type, |
| 1035 | 1042 | .is_non_null, |
| 1036 | .is_null, | |
| 1037 | 1043 | .is_non_null_ptr, |
| 1038 | .is_null_ptr, | |
| 1039 | .is_err, | |
| 1040 | .is_err_ptr, | |
| 1044 | .is_non_err, | |
| 1045 | .is_non_err_ptr, | |
| 1041 | 1046 | .mod_rem, |
| 1042 | 1047 | .mul, |
| 1043 | 1048 | .mulwrap, |
| ... | ... | @@ -1193,6 +1198,7 @@ pub const Inst = struct { |
| 1193 | 1198 | .@"resume", |
| 1194 | 1199 | .@"await", |
| 1195 | 1200 | .await_nosuspend, |
| 1201 | .ret_err_value_code, | |
| 1196 | 1202 | .extended, |
| 1197 | 1203 | => false, |
| 1198 | 1204 | |
| ... | ... | @@ -1203,6 +1209,7 @@ pub const Inst = struct { |
| 1203 | 1209 | .compile_error, |
| 1204 | 1210 | .ret_node, |
| 1205 | 1211 | .ret_coerce, |
| 1212 | .ret_err_value, | |
| 1206 | 1213 | .@"unreachable", |
| 1207 | 1214 | .repeat, |
| 1208 | 1215 | .repeat_inline, |
| ... | ... | @@ -1291,11 +1298,9 @@ pub const Inst = struct { |
| 1291 | 1298 | .float128 = .pl_node, |
| 1292 | 1299 | .int_type = .int_type, |
| 1293 | 1300 | .is_non_null = .un_node, |
| 1294 | .is_null = .un_node, | |
| 1295 | 1301 | .is_non_null_ptr = .un_node, |
| 1296 | .is_null_ptr = .un_node, | |
| 1297 | .is_err = .un_node, | |
| 1298 | .is_err_ptr = .un_node, | |
| 1302 | .is_non_err = .un_node, | |
| 1303 | .is_non_err_ptr = .un_node, | |
| 1299 | 1304 | .loop = .pl_node, |
| 1300 | 1305 | .repeat = .node, |
| 1301 | 1306 | .repeat_inline = .node, |
| ... | ... | @@ -1307,6 +1312,8 @@ pub const Inst = struct { |
| 1307 | 1312 | .ref = .un_tok, |
| 1308 | 1313 | .ret_node = .un_node, |
| 1309 | 1314 | .ret_coerce = .un_tok, |
| 1315 | .ret_err_value = .str_tok, | |
| 1316 | .ret_err_value_code = .str_tok, | |
| 1310 | 1317 | .ptr_type_simple = .ptr_type_simple, |
| 1311 | 1318 | .ptr_type = .ptr_type, |
| 1312 | 1319 | .slice_start = .pl_node, |
| ... | ... | @@ -2840,11 +2847,9 @@ const Writer = struct { |
| 2840 | 2847 | .err_union_code, |
| 2841 | 2848 | .err_union_code_ptr, |
| 2842 | 2849 | .is_non_null, |
| 2843 | .is_null, | |
| 2844 | 2850 | .is_non_null_ptr, |
| 2845 | .is_null_ptr, | |
| 2846 | .is_err, | |
| 2847 | .is_err_ptr, | |
| 2851 | .is_non_err, | |
| 2852 | .is_non_err_ptr, | |
| 2848 | 2853 | .typeof, |
| 2849 | 2854 | .typeof_elem, |
| 2850 | 2855 | .struct_init_empty, |
| ... | ... | @@ -3077,6 +3082,8 @@ const Writer = struct { |
| 3077 | 3082 | .decl_val, |
| 3078 | 3083 | .import, |
| 3079 | 3084 | .arg, |
| 3085 | .ret_err_value, | |
| 3086 | .ret_err_value_code, | |
| 3080 | 3087 | => try self.writeStrTok(stream, inst), |
| 3081 | 3088 | |
| 3082 | 3089 | .func => try self.writeFunc(stream, inst, false), |
src/air.zig+22-12| ... | ... | @@ -90,8 +90,12 @@ pub const Inst = struct { |
| 90 | 90 | is_non_null_ptr, |
| 91 | 91 | /// E!T => bool |
| 92 | 92 | is_err, |
| 93 | /// E!T => bool (inverted logic) | |
| 94 | is_non_err, | |
| 93 | 95 | /// *E!T => bool |
| 94 | 96 | is_err_ptr, |
| 97 | /// *E!T => bool (inverted logic) | |
| 98 | is_non_err_ptr, | |
| 95 | 99 | bool_and, |
| 96 | 100 | bool_or, |
| 97 | 101 | /// Read a value from a pointer. |
| ... | ... | @@ -154,7 +158,9 @@ pub const Inst = struct { |
| 154 | 158 | .is_null, |
| 155 | 159 | .is_null_ptr, |
| 156 | 160 | .is_err, |
| 161 | .is_non_err, | |
| 157 | 162 | .is_err_ptr, |
| 163 | .is_non_err_ptr, | |
| 158 | 164 | .ptrtoint, |
| 159 | 165 | .floatcast, |
| 160 | 166 | .intcast, |
| ... | ... | @@ -672,15 +678,15 @@ pub const Body = struct { |
| 672 | 678 | /// For debugging purposes, prints a function representation to stderr. |
| 673 | 679 | pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { |
| 674 | 680 | const allocator = old_module.gpa; |
| 675 | var ctx: DumpTzir = .{ | |
| 681 | var ctx: DumpAir = .{ | |
| 676 | 682 | .allocator = allocator, |
| 677 | 683 | .arena = std.heap.ArenaAllocator.init(allocator), |
| 678 | 684 | .old_module = &old_module, |
| 679 | 685 | .module_fn = module_fn, |
| 680 | 686 | .indent = 2, |
| 681 | .inst_table = DumpTzir.InstTable.init(allocator), | |
| 682 | .partial_inst_table = DumpTzir.InstTable.init(allocator), | |
| 683 | .const_table = DumpTzir.InstTable.init(allocator), | |
| 687 | .inst_table = DumpAir.InstTable.init(allocator), | |
| 688 | .partial_inst_table = DumpAir.InstTable.init(allocator), | |
| 689 | .const_table = DumpAir.InstTable.init(allocator), | |
| 684 | 690 | }; |
| 685 | 691 | defer ctx.inst_table.deinit(); |
| 686 | 692 | defer ctx.partial_inst_table.deinit(); |
| ... | ... | @@ -695,12 +701,12 @@ pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { |
| 695 | 701 | .dependency_failure => std.debug.print("(dependency_failure)", .{}), |
| 696 | 702 | .success => { |
| 697 | 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 | 710 | allocator: *std.mem.Allocator, |
| 705 | 711 | arena: std.heap.ArenaAllocator, |
| 706 | 712 | old_module: *const Module, |
| ... | ... | @@ -718,7 +724,7 @@ const DumpTzir = struct { |
| 718 | 724 | /// TODO: Improve this code to include a stack of Body and store the instructions |
| 719 | 725 | /// in there. Now we are putting all the instructions in a function local table, |
| 720 | 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 | 728 | // First pass to pre-populate the table so that we can show even invalid references. |
| 723 | 729 | // Must iterate the same order we iterate the second time. |
| 724 | 730 | // We also look for constants and put them in the const_table. |
| ... | ... | @@ -737,7 +743,7 @@ const DumpTzir = struct { |
| 737 | 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 | 747 | for (body.instructions) |inst| { |
| 742 | 748 | try dtz.inst_table.put(inst, dtz.next_index); |
| 743 | 749 | dtz.next_index += 1; |
| ... | ... | @@ -759,7 +765,9 @@ const DumpTzir = struct { |
| 759 | 765 | .is_null, |
| 760 | 766 | .is_null_ptr, |
| 761 | 767 | .is_err, |
| 768 | .is_non_err, | |
| 762 | 769 | .is_err_ptr, |
| 770 | .is_non_err_ptr, | |
| 763 | 771 | .ptrtoint, |
| 764 | 772 | .floatcast, |
| 765 | 773 | .intcast, |
| ... | ... | @@ -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 | 877 | for (body.instructions) |inst| { |
| 870 | 878 | const my_index = dtz.next_partial_index; |
| 871 | 879 | try dtz.partial_inst_table.put(inst, my_index); |
| ... | ... | @@ -888,11 +896,13 @@ const DumpTzir = struct { |
| 888 | 896 | .bitcast, |
| 889 | 897 | .not, |
| 890 | 898 | .is_non_null, |
| 891 | .is_null, | |
| 892 | 899 | .is_non_null_ptr, |
| 900 | .is_null, | |
| 893 | 901 | .is_null_ptr, |
| 894 | 902 | .is_err, |
| 895 | 903 | .is_err_ptr, |
| 904 | .is_non_err, | |
| 905 | .is_non_err_ptr, | |
| 896 | 906 | .ptrtoint, |
| 897 | 907 | .floatcast, |
| 898 | 908 | .intcast, |
| ... | ... | @@ -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 | 1164 | if (dtz.partial_inst_table.get(inst)) |operand_index| { |
| 1155 | 1165 | try writer.print("%{d}", .{operand_index}); |
| 1156 | 1166 | return null; |
| ... | ... | @@ -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 | 1180 | if (operand.tag == .constant) { |
| 1171 | 1181 | try dtz.const_table.put(operand, dtz.next_const_index); |
| 1172 | 1182 | dtz.next_const_index += 1; |
src/codegen.zig+141-85| ... | ... | @@ -142,40 +142,52 @@ pub fn generateSymbol( |
| 142 | 142 | ), |
| 143 | 143 | }; |
| 144 | 144 | }, |
| 145 | .Pointer => { | |
| 146 | // TODO populate .debug_info for the pointer | |
| 147 | if (typed_value.val.castTag(.decl_ref)) |payload| { | |
| 148 | const decl = payload.data; | |
| 149 | if (decl.analysis != .complete) return error.AnalysisFail; | |
| 150 | // TODO handle the dependency of this symbol on the decl's vaddr. | |
| 151 | // If the decl changes vaddr, then this symbol needs to get regenerated. | |
| 152 | const vaddr = bin_file.getDeclVAddr(decl); | |
| 153 | const endian = bin_file.options.target.cpu.arch.endian(); | |
| 154 | switch (bin_file.options.target.cpu.arch.ptrBitWidth()) { | |
| 155 | 16 => { | |
| 156 | try code.resize(2); | |
| 157 | mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian); | |
| 158 | }, | |
| 159 | 32 => { | |
| 160 | try code.resize(4); | |
| 161 | mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian); | |
| 162 | }, | |
| 163 | 64 => { | |
| 164 | try code.resize(8); | |
| 165 | mem.writeInt(u64, code.items[0..8], vaddr, endian); | |
| 166 | }, | |
| 167 | else => unreachable, | |
| 145 | .Pointer => switch (typed_value.ty.ptrSize()) { | |
| 146 | .Slice => { | |
| 147 | return Result{ | |
| 148 | .fail = try ErrorMsg.create( | |
| 149 | bin_file.allocator, | |
| 150 | src_loc, | |
| 151 | "TODO implement generateSymbol for slice {}", | |
| 152 | .{typed_value.val}, | |
| 153 | ), | |
| 154 | }; | |
| 155 | }, | |
| 156 | else => { | |
| 157 | // TODO populate .debug_info for the pointer | |
| 158 | if (typed_value.val.castTag(.decl_ref)) |payload| { | |
| 159 | const decl = payload.data; | |
| 160 | if (decl.analysis != .complete) return error.AnalysisFail; | |
| 161 | // TODO handle the dependency of this symbol on the decl's vaddr. | |
| 162 | // If the decl changes vaddr, then this symbol needs to get regenerated. | |
| 163 | const vaddr = bin_file.getDeclVAddr(decl); | |
| 164 | const endian = bin_file.options.target.cpu.arch.endian(); | |
| 165 | switch (bin_file.options.target.cpu.arch.ptrBitWidth()) { | |
| 166 | 16 => { | |
| 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 = {} }; | |
| 170 | } | |
| 171 | return Result{ | |
| 172 | .fail = try ErrorMsg.create( | |
| 173 | bin_file.allocator, | |
| 174 | src_loc, | |
| 175 | "TODO implement generateSymbol for pointer {}", | |
| 176 | .{typed_value.val}, | |
| 177 | ), | |
| 178 | }; | |
| 182 | return Result{ | |
| 183 | .fail = try ErrorMsg.create( | |
| 184 | bin_file.allocator, | |
| 185 | src_loc, | |
| 186 | "TODO implement generateSymbol for pointer {}", | |
| 187 | .{typed_value.val}, | |
| 188 | ), | |
| 189 | }; | |
| 190 | }, | |
| 179 | 191 | }, |
| 180 | 192 | .Int => { |
| 181 | 193 | // TODO populate .debug_info for the integer |
| ... | ... | @@ -847,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 847 | 859 | .is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?), |
| 848 | 860 | .is_null => return self.genIsNull(inst.castTag(.is_null).?), |
| 849 | 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 | 864 | .is_err => return self.genIsErr(inst.castTag(.is_err).?), |
| 851 | 865 | .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?), |
| 852 | 866 | .load => return self.genLoad(inst.castTag(.load).?), |
| ... | ... | @@ -2244,10 +2258,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2244 | 2258 | try self.register_manager.getReg(reg, null); |
| 2245 | 2259 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); |
| 2246 | 2260 | }, |
| 2247 | .stack_offset => { | |
| 2261 | .stack_offset => |off| { | |
| 2248 | 2262 | // Here we need to emit instructions like this: |
| 2249 | 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 | 2266 | .ptr_stack_offset => { |
| 2253 | 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 | 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 | 2987 | fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue { |
| 2964 | 2988 | // A loop is a setup to be able to jump back to the beginning. |
| 2965 | 2989 | const start_index = self.code.items.len; |
| ... | ... | @@ -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| { | |
| 3448 | _ = code_offset; | |
| 3449 | return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{}); | |
| 3471 | .embedded_in_code => { | |
| 3472 | // TODO this and `.stack_offset` below need to get improved to support types greater than | |
| 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 | 3477 | .register => |reg| { |
| 3452 | 3478 | try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89); |
| ... | ... | @@ -3456,6 +3482,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3456 | 3482 | return self.fail(src, "TODO implement set stack variable from memory vaddr", .{}); |
| 3457 | 3483 | }, |
| 3458 | 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 | 3488 | if (stack_offset == off) |
| 3460 | 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 | 4190 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 4162 | 4191 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 4163 | 4192 | switch (typed_value.ty.zigTypeTag()) { |
| 4164 | .Pointer => { | |
| 4165 | if (typed_value.val.castTag(.decl_ref)) |payload| { | |
| 4166 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | |
| 4167 | const decl = payload.data; | |
| 4168 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 4169 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | |
| 4170 | return MCValue{ .memory = got_addr }; | |
| 4171 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 4172 | const decl = payload.data; | |
| 4173 | const got_addr = blk: { | |
| 4174 | const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; | |
| 4175 | const got = seg.sections.items[macho_file.got_section_index.?]; | |
| 4176 | break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes; | |
| 4177 | }; | |
| 4178 | return MCValue{ .memory = got_addr }; | |
| 4179 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | |
| 4180 | const decl = payload.data; | |
| 4181 | const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; | |
| 4182 | return MCValue{ .memory = got_addr }; | |
| 4183 | } else { | |
| 4184 | return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{}); | |
| 4193 | .Pointer => switch (typed_value.ty.ptrSize()) { | |
| 4194 | .Slice => { | |
| 4195 | var buf: Type.Payload.ElemType = undefined; | |
| 4196 | const ptr_type = typed_value.ty.slicePtrFieldType(&buf); | |
| 4197 | const ptr_mcv = try self.genTypedValue(src, .{ .ty = ptr_type, .val = typed_value.val }); | |
| 4198 | const slice_len = typed_value.val.sliceLen(); | |
| 4199 | // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean | |
| 4200 | // the Sema code needs to use anonymous Decls or alloca instructions to store data. | |
| 4201 | const ptr_imm = ptr_mcv.memory; | |
| 4202 | _ = slice_len; | |
| 4203 | _ = ptr_imm; | |
| 4204 | // We need more general support for const data being stored in memory to make this work. | |
| 4205 | return self.fail(src, "TODO codegen for const slices", .{}); | |
| 4206 | }, | |
| 4207 | else => { | |
| 4208 | if (typed_value.val.castTag(.decl_ref)) |payload| { | |
| 4209 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | |
| 4210 | const decl = payload.data; | |
| 4211 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 4212 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | |
| 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 | } | |
| 4187 | if (typed_value.val.tag() == .int_u64) { | |
| 4188 | return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; | |
| 4189 | } | |
| 4190 | return self.fail(src, "TODO codegen more kinds of const pointers", .{}); | |
| 4230 | if (typed_value.val.tag() == .int_u64) { | |
| 4231 | return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; | |
| 4232 | } | |
| 4233 | return self.fail(src, "TODO codegen more kinds of const pointers", .{}); | |
| 4234 | }, | |
| 4191 | 4235 | }, |
| 4192 | 4236 | .Int => { |
| 4193 | 4237 | const info = typed_value.ty.intInfo(self.target.*); |
| ... | ... | @@ -4264,27 +4308,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4264 | 4308 | var next_stack_offset: u32 = 0; |
| 4265 | 4309 | |
| 4266 | 4310 | for (param_types) |ty, i| { |
| 4267 | switch (ty.zigTypeTag()) { | |
| 4268 | .Bool, .Int => { | |
| 4269 | if (!ty.hasCodeGenBits()) { | |
| 4270 | assert(cc != .C); | |
| 4271 | result.args[i] = .{ .none = {} }; | |
| 4272 | } else { | |
| 4273 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | |
| 4274 | if (next_int_reg >= c_abi_int_param_regs.len) { | |
| 4275 | result.args[i] = .{ .stack_offset = next_stack_offset }; | |
| 4276 | next_stack_offset += param_size; | |
| 4277 | } else { | |
| 4278 | const aliased_reg = registerAlias( | |
| 4279 | c_abi_int_param_regs[next_int_reg], | |
| 4280 | param_size, | |
| 4281 | ); | |
| 4282 | result.args[i] = .{ .register = aliased_reg }; | |
| 4283 | next_int_reg += 1; | |
| 4284 | } | |
| 4285 | } | |
| 4286 | }, | |
| 4287 | else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}), | |
| 4311 | if (!ty.hasCodeGenBits()) { | |
| 4312 | assert(cc != .C); | |
| 4313 | result.args[i] = .{ .none = {} }; | |
| 4314 | continue; | |
| 4315 | } | |
| 4316 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | |
| 4317 | const pass_in_reg = switch (ty.zigTypeTag()) { | |
| 4318 | .Bool => true, | |
| 4319 | .Int => param_size <= 8, | |
| 4320 | .Pointer => ty.ptrSize() != .Slice, | |
| 4321 | .Optional => ty.isPtrLikeOptional(), | |
| 4322 | else => false, | |
| 4323 | }; | |
| 4324 | if (pass_in_reg) { | |
| 4325 | if (next_int_reg >= c_abi_int_param_regs.len) { | |
| 4326 | result.args[i] = .{ .stack_offset = next_stack_offset }; | |
| 4327 | next_stack_offset += param_size; | |
| 4328 | } else { | |
| 4329 | const aliased_reg = registerAlias( | |
| 4330 | c_abi_int_param_regs[next_int_reg], | |
| 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 | 4346 | result.stack_byte_count = next_stack_offset; |
src/codegen/c.zig+285-68| ... | ... | @@ -39,7 +39,12 @@ const BlockData = struct { |
| 39 | 39 | }; |
| 40 | 40 | |
| 41 | 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 | 49 | fn formatTypeAsCIdentifier( |
| 45 | 50 | data: Type, |
| ... | ... | @@ -151,14 +156,49 @@ pub const Object = struct { |
| 151 | 156 | render_ty = render_ty.elemType(); |
| 152 | 157 | } |
| 153 | 158 | |
| 154 | try o.dg.renderType(w, render_ty); | |
| 155 | ||
| 156 | const const_prefix = switch (mutability) { | |
| 157 | .Const => "const ", | |
| 158 | .Mut => "", | |
| 159 | }; | |
| 160 | try w.print(" {s}", .{const_prefix}); | |
| 161 | try o.writeCValue(w, name); | |
| 159 | if (render_ty.zigTypeTag() == .Fn) { | |
| 160 | const ret_ty = render_ty.fnReturnType(); | |
| 161 | if (ret_ty.zigTypeTag() == .NoReturn) { | |
| 162 | // noreturn attribute is not allowed here. | |
| 163 | try w.writeAll("void"); | |
| 164 | } else { | |
| 165 | try o.dg.renderType(w, ret_ty); | |
| 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 | 202 | try w.writeAll(suffix.items); |
| 163 | 203 | } |
| 164 | 204 | }; |
| ... | ... | @@ -196,35 +236,72 @@ pub const DeclGen = struct { |
| 196 | 236 | return writer.print("{d}", .{val.toSignedInt()}); |
| 197 | 237 | return writer.print("{d}", .{val.toUnsignedInt()}); |
| 198 | 238 | }, |
| 199 | .Pointer => switch (val.tag()) { | |
| 200 | .null_value, .zero => try writer.writeAll("NULL"), | |
| 201 | .one => try writer.writeAll("1"), | |
| 202 | .decl_ref => { | |
| 203 | const decl = val.castTag(.decl_ref).?.data; | |
| 204 | ||
| 205 | // Determine if we must pointer cast. | |
| 206 | assert(decl.has_tv); | |
| 207 | if (t.eql(decl.ty)) { | |
| 208 | try writer.print("&{s}", .{decl.name}); | |
| 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}); | |
| 239 | .Pointer => switch (t.ptrSize()) { | |
| 240 | .Slice => { | |
| 241 | try writer.writeByte('('); | |
| 242 | try dg.renderType(writer, t); | |
| 243 | try writer.writeAll("){"); | |
| 244 | var buf: Type.Payload.ElemType = undefined; | |
| 245 | try dg.renderValue(writer, t.slicePtrFieldType(&buf), val); | |
| 246 | try writer.writeAll(", "); | |
| 247 | try writer.print("{d}", .{val.sliceLen()}); | |
| 248 | try writer.writeAll("}"); | |
| 218 | 249 | }, |
| 219 | .extern_fn => { | |
| 220 | const decl = val.castTag(.extern_fn).?.data; | |
| 221 | try writer.print("{s}", .{decl.name}); | |
| 250 | else => switch (val.tag()) { | |
| 251 | .null_value, .zero => try writer.writeAll("NULL"), | |
| 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 | 306 | .Array => { |
| 230 | 307 | // First try specific tag representations for more efficiency. |
| ... | ... | @@ -283,6 +360,12 @@ pub const DeclGen = struct { |
| 283 | 360 | const error_type = t.errorUnionSet(); |
| 284 | 361 | const payload_type = t.errorUnionChild(); |
| 285 | 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 | 369 | try writer.writeByte('('); |
| 287 | 370 | try dg.renderType(writer, t); |
| 288 | 371 | try writer.writeAll("){"); |
| ... | ... | @@ -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 | 441 | else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{ |
| 333 | 442 | @tagName(e), |
| 334 | 443 | }), |
| ... | ... | @@ -339,6 +448,12 @@ pub const DeclGen = struct { |
| 339 | 448 | if (!is_global) { |
| 340 | 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 | 457 | try dg.renderType(w, dg.decl.ty.fnReturnType()); |
| 343 | 458 | const decl_name = mem.span(dg.decl.name); |
| 344 | 459 | try w.print(" {s}(", .{decl_name}); |
| ... | ... | @@ -413,7 +528,35 @@ pub const DeclGen = struct { |
| 413 | 528 | |
| 414 | 529 | .Pointer => { |
| 415 | 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 | 560 | } else { |
| 418 | 561 | try dg.renderType(w, t.elemType()); |
| 419 | 562 | try w.writeAll(" *"); |
| ... | ... | @@ -446,13 +589,13 @@ pub const DeclGen = struct { |
| 446 | 589 | try dg.renderType(bw, child_type); |
| 447 | 590 | try bw.writeAll(" payload; bool is_null; } "); |
| 448 | 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 | 594 | const rendered = buffer.toOwnedSlice(); |
| 452 | 595 | errdefer dg.typedefs.allocator.free(rendered); |
| 453 | 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 | 599 | try w.writeAll(name); |
| 457 | 600 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 458 | 601 | }, |
| ... | ... | @@ -465,7 +608,11 @@ pub const DeclGen = struct { |
| 465 | 608 | return w.writeAll(some.name); |
| 466 | 609 | } |
| 467 | 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 | 617 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); |
| 471 | 618 | defer buffer.deinit(); |
| ... | ... | @@ -475,13 +622,20 @@ pub const DeclGen = struct { |
| 475 | 622 | try dg.renderType(bw, child_type); |
| 476 | 623 | try bw.writeAll(" payload; uint16_t error; } "); |
| 477 | 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 | 634 | const rendered = buffer.toOwnedSlice(); |
| 481 | 635 | errdefer dg.typedefs.allocator.free(rendered); |
| 482 | 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 | 639 | try w.writeAll(name); |
| 486 | 640 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 487 | 641 | }, |
| ... | ... | @@ -514,7 +668,7 @@ pub const DeclGen = struct { |
| 514 | 668 | errdefer dg.typedefs.allocator.free(rendered); |
| 515 | 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 | 672 | try w.writeAll(name); |
| 519 | 673 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 520 | 674 | }, |
| ... | ... | @@ -526,7 +680,28 @@ pub const DeclGen = struct { |
| 526 | 680 | try dg.renderType(w, int_tag_ty); |
| 527 | 681 | }, |
| 528 | 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 | 705 | .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}), |
| 531 | 706 | .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}), |
| 532 | 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 | 744 | .val = o.dg.decl.val, |
| 570 | 745 | }; |
| 571 | 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 | 747 | const func: *Module.Fn = func_payload.data; |
| 581 | try o.indent_writer.insertNewline(); | |
| 582 | try o.dg.renderFunctionSignature(o.writer(), is_global); | |
| 748 | if (func.owner_decl == o.dg.decl) { | |
| 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(' '); | |
| 585 | try genBody(o, func.body); | |
| 757 | try o.indent_writer.insertNewline(); | |
| 758 | try o.dg.renderFunctionSignature(o.writer(), is_global); | |
| 586 | 759 | |
| 587 | try o.indent_writer.insertNewline(); | |
| 588 | } else if (tv.val.tag() == .extern_fn) { | |
| 760 | try o.writer().writeByte(' '); | |
| 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 | 768 | const writer = o.writer(); |
| 590 | 769 | try writer.writeAll("ZIG_EXTERN_C "); |
| 591 | 770 | try o.dg.renderFunctionSignature(writer, true); |
| ... | ... | @@ -644,9 +823,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { |
| 644 | 823 | const is_global = dg.declIsGlobal(tv); |
| 645 | 824 | if (is_global) { |
| 646 | 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 | 830 | else => {}, |
| 652 | 831 | } |
| ... | ... | @@ -726,8 +905,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi |
| 726 | 905 | .ref => try genRef(o, inst.castTag(.ref).?), |
| 727 | 906 | .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?), |
| 728 | 907 | |
| 729 | .is_err => try genIsErr(o, inst.castTag(.is_err).?), | |
| 730 | .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?), | |
| 908 | .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", ".", "!="), | |
| 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 | 913 | .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), |
| 733 | 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 | 1394 | |
| 1214 | 1395 | // *(E!T) -> E NOT *E |
| 1215 | 1396 | fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { |
| 1397 | if (inst.base.isUnused()) | |
| 1398 | return CValue.none; | |
| 1399 | ||
| 1216 | 1400 | const writer = o.writer(); |
| 1217 | 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 | 1416 | const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; |
| 1220 | 1417 | |
| 1221 | 1418 | const local = try o.allocLocal(inst.base.ty, .Const); |
| ... | ... | @@ -1225,10 +1422,19 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { |
| 1225 | 1422 | try writer.print("){s}error;\n", .{maybe_deref}); |
| 1226 | 1423 | return local; |
| 1227 | 1424 | } |
| 1425 | ||
| 1228 | 1426 | fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { |
| 1427 | if (inst.base.isUnused()) | |
| 1428 | return CValue.none; | |
| 1429 | ||
| 1229 | 1430 | const writer = o.writer(); |
| 1230 | 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 | 1438 | const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; |
| 1233 | 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 | 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 | 1493 | const writer = o.writer(); |
| 1282 | const maybe_deref = if (inst.base.tag == .is_err_ptr) "[0]" else ""; | |
| 1283 | 1494 | const operand = try o.resolveInst(inst.operand); |
| 1284 | ||
| 1285 | 1495 | const local = try o.allocLocal(Type.initTag(.bool), .Const); |
| 1286 | try writer.writeAll(" = ("); | |
| 1287 | try o.writeCValue(writer, operand); | |
| 1288 | try writer.print("){s}.error != 0;\n", .{maybe_deref}); | |
| 1496 | const payload_ty = inst.operand.ty.errorUnionChild(); | |
| 1497 | if (!payload_ty.hasCodeGenBits()) { | |
| 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 | 1506 | return local; |
| 1290 | 1507 | } |
| 1291 | 1508 |
src/codegen/wasm.zig+4-5| ... | ... | @@ -814,7 +814,8 @@ pub const Context = struct { |
| 814 | 814 | .constant => unreachable, |
| 815 | 815 | .dbg_stmt => WValue.none, |
| 816 | 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 | 819 | .load => self.genLoad(inst.castTag(.load).?), |
| 819 | 820 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 820 | 821 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), |
| ... | ... | @@ -1278,7 +1279,7 @@ pub const Context = struct { |
| 1278 | 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 | 1283 | const operand = self.resolveInst(inst.operand); |
| 1283 | 1284 | const offset = self.code.items.len; |
| 1284 | 1285 | const writer = self.code.writer(); |
| ... | ... | @@ -1289,9 +1290,7 @@ pub const Context = struct { |
| 1289 | 1290 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 1290 | 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 | // because that means there's an error. | |
| 1294 | try writer.writeByte(wasm.opcode(.i32_ne)); | |
| 1293 | try writer.writeByte(@enumToInt(opcode)); | |
| 1295 | 1294 | |
| 1296 | 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 | 207 | } |
| 208 | 208 | |
| 209 | 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 | 211 | defer typedefs.deinit(); |
| 212 | 212 | |
| 213 | 213 | // Typedefs, forward decls and non-functions first. |
| ... | ... | @@ -217,14 +217,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 217 | 217 | if (!decl.has_tv) continue; |
| 218 | 218 | const buf = buf: { |
| 219 | 219 | if (decl.val.castTag(.function)) |_| { |
| 220 | try typedefs.ensureUnusedCapacity(decl.fn_link.c.typedefs.count()); | |
| 220 | 221 | var it = decl.fn_link.c.typedefs.iterator(); |
| 221 | 222 | while (it.next()) |new| { |
| 222 | if (typedefs.get(new.key_ptr.*)) |previous| { | |
| 223 | try err_typedef_writer.print("typedef {s} {s};\n", .{ previous, new.value_ptr.name }); | |
| 224 | } else { | |
| 225 | try typedefs.ensureCapacity(typedefs.capacity() + 1); | |
| 223 | const gop = typedefs.getOrPutAssumeCapacity(new.key_ptr.*); | |
| 224 | if (!gop.found_existing) { | |
| 226 | 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 | 228 | fn_count += 1; |
src/link/C/zig.h+6| ... | ... | @@ -12,6 +12,12 @@ |
| 12 | 12 | #define zig_threadlocal zig_threadlocal_unavailable |
| 13 | 13 | #endif |
| 14 | 14 | |
| 15 | #if __GNUC__ | |
| 16 | #define ZIG_COLD __attribute__ ((cold)) | |
| 17 | #else | |
| 18 | #define ZIG_COLD | |
| 19 | #endif | |
| 20 | ||
| 15 | 21 | #if __STDC_VERSION__ >= 199901L |
| 16 | 22 | #define ZIG_RESTRICT restrict |
| 17 | 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 | 2505 | abbrev_base_type, |
| 2506 | 2506 | DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1 |
| 2507 | 2507 | 1, // DW.AT_byte_size, DW.FORM_data1 |
| 2508 | 'b', | |
| 2509 | 'o', | |
| 2510 | 'o', | |
| 2511 | 'l', | |
| 2512 | 0, // DW.AT_name, DW.FORM_string | |
| 2508 | 'b', 'o', 'o', 'l', 0, // DW.AT_name, DW.FORM_string | |
| 2513 | 2509 | }); |
| 2514 | 2510 | }, |
| 2515 | 2511 | .Int => { |
| ... | ... | @@ -2526,8 +2522,23 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo |
| 2526 | 2522 | // DW.AT_name, DW.FORM_string |
| 2527 | 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 | 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 | 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 | 58 | .bool => return .Bool, |
| 59 | 59 | .void => return .Void, |
| 60 | 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 | 62 | .comptime_int => return .ComptimeInt, |
| 63 | 63 | .comptime_float => return .ComptimeFloat, |
| 64 | 64 | .noreturn => return .NoReturn, |
| ... | ... | @@ -689,7 +689,15 @@ pub const Type = extern union { |
| 689 | 689 | .optional_single_mut_pointer, |
| 690 | 690 | .optional_single_const_pointer, |
| 691 | 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 | 702 | .int_signed, |
| 695 | 703 | .int_unsigned, |
| ... | ... | @@ -756,6 +764,7 @@ pub const Type = extern union { |
| 756 | 764 | }); |
| 757 | 765 | }, |
| 758 | 766 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), |
| 767 | .error_set_inferred => return self.copyPayloadShallow(allocator, Payload.ErrorSetInferred), | |
| 759 | 768 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), |
| 760 | 769 | .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope), |
| 761 | 770 | .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), |
| ... | ... | @@ -1031,6 +1040,10 @@ pub const Type = extern union { |
| 1031 | 1040 | const error_set = ty.castTag(.error_set).?.data; |
| 1032 | 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 | 1047 | .error_set_single => { |
| 1035 | 1048 | const name = ty.castTag(.error_set_single).?.data; |
| 1036 | 1049 | return writer.print("error{{{s}}}", .{name}); |
| ... | ... | @@ -1144,6 +1157,7 @@ pub const Type = extern union { |
| 1144 | 1157 | .anyerror_void_error_union, |
| 1145 | 1158 | .error_set, |
| 1146 | 1159 | .error_set_single, |
| 1160 | .error_set_inferred, | |
| 1147 | 1161 | .manyptr_u8, |
| 1148 | 1162 | .manyptr_const_u8, |
| 1149 | 1163 | .atomic_ordering, |
| ... | ... | @@ -1161,6 +1175,9 @@ pub const Type = extern union { |
| 1161 | 1175 | .@"struct" => { |
| 1162 | 1176 | // TODO introduce lazy value mechanism |
| 1163 | 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 | 1181 | for (struct_obj.fields.values()) |value| { |
| 1165 | 1182 | if (value.ty.hasCodeGenBits()) |
| 1166 | 1183 | return true; |
| ... | ... | @@ -1348,6 +1365,7 @@ pub const Type = extern union { |
| 1348 | 1365 | .error_set_single, |
| 1349 | 1366 | .anyerror_void_error_union, |
| 1350 | 1367 | .anyerror, |
| 1368 | .error_set_inferred, | |
| 1351 | 1369 | => return 2, // TODO revisit this when we have the concept of the error tag type |
| 1352 | 1370 | |
| 1353 | 1371 | .array, .array_sentinel => return self.elemType().abiAlignment(target), |
| ... | ... | @@ -1580,6 +1598,7 @@ pub const Type = extern union { |
| 1580 | 1598 | .error_set_single, |
| 1581 | 1599 | .anyerror_void_error_union, |
| 1582 | 1600 | .anyerror, |
| 1601 | .error_set_inferred, | |
| 1583 | 1602 | => return 2, // TODO revisit this when we have the concept of the error tag type |
| 1584 | 1603 | |
| 1585 | 1604 | .int_signed, .int_unsigned => { |
| ... | ... | @@ -1744,6 +1763,7 @@ pub const Type = extern union { |
| 1744 | 1763 | .error_set_single, |
| 1745 | 1764 | .anyerror_void_error_union, |
| 1746 | 1765 | .anyerror, |
| 1766 | .error_set_inferred, | |
| 1747 | 1767 | => return 16, // TODO revisit this when we have the concept of the error tag type |
| 1748 | 1768 | |
| 1749 | 1769 | .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data, |
| ... | ... | @@ -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 | 1928 | pub fn isConstPtr(self: Type) bool { |
| 1867 | 1929 | return switch (self.tag()) { |
| 1868 | 1930 | .single_const_pointer, |
| ... | ... | @@ -1915,7 +1977,10 @@ pub const Type = extern union { |
| 1915 | 1977 | /// Asserts that the type is an optional |
| 1916 | 1978 | pub fn isPtrLikeOptional(self: Type) bool { |
| 1917 | 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 | 1984 | .optional => { |
| 1920 | 1985 | var buf: Payload.ElemType = undefined; |
| 1921 | 1986 | const child_type = self.optionalChild(&buf); |
| ... | ... | @@ -2400,6 +2465,7 @@ pub const Type = extern union { |
| 2400 | 2465 | .error_union, |
| 2401 | 2466 | .error_set, |
| 2402 | 2467 | .error_set_single, |
| 2468 | .error_set_inferred, | |
| 2403 | 2469 | .@"opaque", |
| 2404 | 2470 | .var_args_param, |
| 2405 | 2471 | .manyptr_u8, |
| ... | ... | @@ -2892,6 +2958,8 @@ pub const Type = extern union { |
| 2892 | 2958 | anyframe_T, |
| 2893 | 2959 | error_set, |
| 2894 | 2960 | error_set_single, |
| 2961 | /// The type is the inferred error set of a specific function. | |
| 2962 | error_set_inferred, | |
| 2895 | 2963 | empty_struct, |
| 2896 | 2964 | @"opaque", |
| 2897 | 2965 | @"struct", |
| ... | ... | @@ -2989,6 +3057,7 @@ pub const Type = extern union { |
| 2989 | 3057 | => Payload.Bits, |
| 2990 | 3058 | |
| 2991 | 3059 | .error_set => Payload.ErrorSet, |
| 3060 | .error_set_inferred => Payload.ErrorSetInferred, | |
| 2992 | 3061 | |
| 2993 | 3062 | .array, .vector => Payload.Array, |
| 2994 | 3063 | .array_sentinel => Payload.ArraySentinel, |
| ... | ... | @@ -3081,6 +3150,16 @@ pub const Type = extern union { |
| 3081 | 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 | 3163 | pub const Pointer = struct { |
| 3085 | 3164 | pub const base_tag = Tag.pointer; |
| 3086 | 3165 |
src/value.zig+22-5| ... | ... | @@ -483,13 +483,13 @@ pub const Value = extern union { |
| 483 | 483 | /// TODO this should become a debug dump() function. In order to print values in a meaningful way |
| 484 | 484 | /// we also need access to the type. |
| 485 | 485 | pub fn format( |
| 486 | self: Value, | |
| 486 | start_val: Value, | |
| 487 | 487 | comptime fmt: []const u8, |
| 488 | 488 | options: std.fmt.FormatOptions, |
| 489 | 489 | out_stream: anytype, |
| 490 | 490 | ) !void { |
| 491 | 491 | comptime assert(fmt.len == 0); |
| 492 | var val = self; | |
| 492 | var val = start_val; | |
| 493 | 493 | while (true) switch (val.tag()) { |
| 494 | 494 | .u8_type => return out_stream.writeAll("u8"), |
| 495 | 495 | .i8_type => return out_stream.writeAll("i8"), |
| ... | ... | @@ -598,9 +598,9 @@ pub const Value = extern union { |
| 598 | 598 | val = field_ptr.container_ptr; |
| 599 | 599 | }, |
| 600 | 600 | .empty_array => return out_stream.writeAll(".{}"), |
| 601 | .enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(self.castTag(.enum_literal).?.data)}), | |
| 602 | .enum_field_index => return out_stream.print("(enum field {d})", .{self.castTag(.enum_field_index).?.data}), | |
| 603 | .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(self.castTag(.bytes).?.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})", .{val.castTag(.enum_field_index).?.data}), | |
| 603 | .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}), | |
| 604 | 604 | .repeated => { |
| 605 | 605 | try out_stream.writeAll("(repeated) "); |
| 606 | 606 | val = val.castTag(.repeated).?.data; |
| ... | ... | @@ -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 | 1356 | /// Asserts the value is a single-item pointer to an array, or an array, |
| 1340 | 1357 | /// or an unknown-length pointer, and returns the element value at the index. |
| 1341 | 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 | 804 | }); |
| 805 | 805 | } |
| 806 | 806 | |
| 807 | ctx.c("empty start function", linux_x64, | |
| 808 | \\export fn _start() noreturn { | |
| 809 | \\ unreachable; | |
| 810 | \\} | |
| 811 | , | |
| 812 | \\ZIG_EXTERN_C zig_noreturn void _start(void); | |
| 813 | \\ | |
| 814 | \\zig_noreturn void _start(void) { | |
| 815 | \\ zig_breakpoint(); | |
| 816 | \\ zig_unreachable(); | |
| 817 | \\} | |
| 818 | \\ | |
| 819 | ); | |
| 807 | { | |
| 808 | var case = ctx.exeFromCompiledC("inferred error sets", .{}); | |
| 809 | ||
| 810 | case.addCompareOutput( | |
| 811 | \\pub export fn main() c_int { | |
| 812 | \\ if (foo()) |_| { | |
| 813 | \\ @panic("test fail"); | |
| 814 | \\ } else |err| { | |
| 815 | \\ if (err != error.ItBroke) { | |
| 816 | \\ @panic("test fail"); | |
| 817 | \\ } | |
| 818 | \\ } | |
| 819 | \\ return 0; | |
| 820 | \\} | |
| 821 | \\fn foo() !void { | |
| 822 | \\ return error.ItBroke; | |
| 823 | \\} | |
| 824 | , ""); | |
| 825 | } | |
| 826 | ||
| 820 | 827 | ctx.h("simple header", linux_x64, |
| 821 | 828 | \\export fn start() void{} |
| 822 | 829 | , |
test/stage2/wasm.zig-2| ... | ... | @@ -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 | 590 | var case = ctx.exe("wasm error union part 2", wasi); |
| 593 | 591 | |
| 594 | 592 | case.addCompareOutput( |