authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-08 02:04:53-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-07-08 02:04:53-04:00
log62d27fcfb687e3ab1f10c72513e19529d8ffceed
tree2885da99ca325959e40f417346aca63ddef4fb31
parent7935e83b1d5d29cca058597ebdac6dfd012a790a
parentc2e66d9bab396a69514ec7c3c41fb0404e542f21
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9325 from ziglang/stage2-inferred-error-sets

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_
677677/// therefore must be kept in sync with the compiler implementation.
678678pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn {
679679 @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 }
680687 if (@hasDecl(root, "os") and @hasDecl(root.os, "panic")) {
681688 root.os.panic(msg, error_return_trace);
682689 unreachable;
lib/std/hash_map.zig+26-6
......@@ -483,10 +483,20 @@ pub fn HashMap(
483483 return self.unmanaged.getOrPutValueContext(self.allocator, key, value, self.ctx);
484484 }
485485
486 /// Deprecated: call `ensureUnusedCapacity` or `ensureTotalCapacity`.
487 pub const ensureCapacity = ensureTotalCapacity;
488
486489 /// Increases capacity, guaranteeing that insertions up until the
487490 /// `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);
490500 }
491501
492502 /// Returns the number of total elements which may be present before it is
......@@ -821,16 +831,26 @@ pub fn HashMapUnmanaged(
821831 return new_cap;
822832 }
823833
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 {
825838 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);
828841 }
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 {
830843 if (new_size > self.size)
831844 try self.growIfNeeded(allocator, new_size - self.size, ctx);
832845 }
833846
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
834854 pub fn clearRetainingCapacity(self: *Self) void {
835855 if (self.metadata) |_| {
836856 self.initMetadatas();
src/AstGen.zig+164-57
......@@ -786,7 +786,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
786786 rl,
787787 node,
788788 node_datas[node].lhs,
789 .is_err_ptr,
789 .is_non_err_ptr,
790790 .err_union_payload_unsafe_ptr,
791791 .err_union_code_ptr,
792792 node_datas[node].rhs,
......@@ -798,7 +798,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
798798 rl,
799799 node,
800800 node_datas[node].lhs,
801 .is_err,
801 .is_non_err,
802802 .err_union_payload_unsafe,
803803 .err_union_code,
804804 node_datas[node].rhs,
......@@ -813,7 +813,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
813813 rl,
814814 node,
815815 node_datas[node].lhs,
816 .is_null_ptr,
816 .is_non_null_ptr,
817817 .optional_payload_unsafe_ptr,
818818 undefined,
819819 node_datas[node].rhs,
......@@ -825,7 +825,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
825825 rl,
826826 node,
827827 node_datas[node].lhs,
828 .is_null,
828 .is_non_null,
829829 .optional_payload_unsafe,
830830 undefined,
831831 node_datas[node].rhs,
......@@ -1860,7 +1860,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const ast.Nod
18601860 }
18611861 }
18621862
1863 try genDefers(gz, parent_scope, scope, .none);
1863 try genDefers(gz, parent_scope, scope, .normal_only);
18641864 try checkUsed(gz, parent_scope, scope);
18651865}
18661866
......@@ -1948,11 +1948,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
19481948 .float128,
19491949 .int_type,
19501950 .is_non_null,
1951 .is_null,
19521951 .is_non_null_ptr,
1953 .is_null_ptr,
1954 .is_err,
1955 .is_err_ptr,
1952 .is_non_err,
1953 .is_non_err_ptr,
19561954 .mod_rem,
19571955 .mul,
19581956 .mulwrap,
......@@ -2102,6 +2100,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
21022100 .@"resume",
21032101 .@"await",
21042102 .await_nosuspend,
2103 .ret_err_value_code,
21052104 .extended,
21062105 => break :b false,
21072106
......@@ -2113,6 +2112,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
21132112 .compile_error,
21142113 .ret_node,
21152114 .ret_coerce,
2115 .ret_err_value,
21162116 .@"unreachable",
21172117 .repeat,
21182118 .repeat_inline,
......@@ -2162,13 +2162,63 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
21622162 return noreturn_src_node;
21632163}
21642164
2165fn 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
2210const DefersToEmit = union(enum) {
2211 both: Zir.Inst.Ref, // err code
2212 both_sans_err,
2213 normal_only,
2214};
2215
21652216fn genDefers(
21662217 gz: *GenZir,
21672218 outer_scope: *Scope,
21682219 inner_scope: *Scope,
2169 err_code: Zir.Inst.Ref,
2220 which_ones: DefersToEmit,
21702221) InnerError!void {
2171 _ = err_code;
21722222 const astgen = gz.astgen;
21732223 const tree = astgen.tree;
21742224 const node_datas = tree.nodes.items(.data);
......@@ -2191,12 +2241,37 @@ fn genDefers(
21912241 .defer_error => {
21922242 const defer_scope = scope.cast(Scope.Defer).?;
21932243 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 }
22002275 },
22012276 .namespace => unreachable,
22022277 .top => unreachable,
......@@ -4544,8 +4619,8 @@ fn tryExpr(
45444619 };
45454620 const err_ops = switch (rl) {
45464621 // 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 },
45494624 // zig fmt: on
45504625 };
45514626 // This could be a pointer or value depending on the `operand_rl` parameter.
......@@ -4563,21 +4638,21 @@ fn tryExpr(
45634638 var then_scope = parent_gz.makeSubBlock(scope);
45644639 defer then_scope.instructions.deinit(astgen.gpa);
45654640
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
45734641 block_scope.break_count += 1;
45744642 // 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) {
45774645 .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),
45794647 };
45804648
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
45814656 return finishThenElseBlock(
45824657 parent_gz,
45834658 rl,
......@@ -4634,18 +4709,28 @@ fn orelseCatchExpr(
46344709 var then_scope = parent_gz.makeSubBlock(scope);
46354710 defer then_scope.instructions.deinit(astgen.gpa);
46364711
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
46374722 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;
46404725 if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
46414726 return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
46424727 }
46434728 const err_name = try astgen.identAsString(payload);
46444729 err_val_scope = .{
4645 .parent = &then_scope.base,
4646 .gen_zir = &then_scope,
4730 .parent = &else_scope.base,
4731 .gen_zir = &else_scope,
46474732 .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),
46494734 .token_src = payload,
46504735 .id_cat = .@"capture",
46514736 };
......@@ -4653,23 +4738,13 @@ fn orelseCatchExpr(
46534738 };
46544739
46554740 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);
46584743
46594744 // We hold off on the break instructions as well as copying the then/else
46604745 // instructions into place until we know whether to keep store_to_block_ptr
46614746 // instructions or not.
46624747
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
46734748 return finishThenElseBlock(
46744749 parent_gz,
46754750 rl,
......@@ -4887,7 +4962,7 @@ fn ifExpr(
48874962 if (if_full.error_token) |_| {
48884963 const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
48894964 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;
48914966 break :c .{
48924967 .inst = err_union,
48934968 .bool_bit = try block_scope.addUnNode(tag, err_union, node),
......@@ -5144,7 +5219,7 @@ fn whileExpr(
51445219 if (while_full.error_token) |_| {
51455220 const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
51465221 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;
51485223 break :c .{
51495224 .inst = err_union,
51505225 .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
60906165 const astgen = gz.astgen;
60916166 const tree = astgen.tree;
60926167 const node_datas = tree.nodes.items(.data);
6168 const node_tags = tree.nodes.items(.tag);
60936169
60946170 if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{});
60956171
6172 const defer_outer = &astgen.fn_block.?.base;
6173
60966174 const operand_node = node_datas[node].lhs;
60976175 if (operand_node == 0) {
60986176 // 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);
61006178 _ = try gz.addUnNode(.ret_node, .void_value, node);
61016179 return Zir.Inst.Ref.unreachable_value;
61026180 }
61036181
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
61046199 const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{
61056200 .ptr = try gz.addNodeExtended(.ret_ptr, node),
61066201 } else .{
......@@ -6111,34 +6206,46 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref
61116206 switch (nodeMayEvalToError(tree, operand_node)) {
61126207 .never => {
61136208 // 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);
61156210 _ = try gz.addUnNode(.ret_node, operand, node);
61166211 return Zir.Inst.Ref.unreachable_value;
61176212 },
61186213 .always => {
61196214 // Value is always an error. Emit both error defers and regular defers.
61206215 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 });
61226217 _ = try gz.addUnNode(.ret_node, operand, node);
61236218 return Zir.Inst.Ref.unreachable_value;
61246219 },
61256220 .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
61266229 // 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);
61286231 const condbr = try gz.addCondBr(.condbr, node);
61296232
61306233 var then_scope = gz.makeSubBlock(scope);
61316234 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);
61346237 _ = try then_scope.addUnNode(.ret_node, operand, node);
61356238
61366239 var else_scope = gz.makeSubBlock(scope);
61376240 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);
61396246 _ = try else_scope.addUnNode(.ret_node, operand, node);
61406247
6141 try setCondBrPayload(condbr, is_err, &then_scope, &else_scope);
6248 try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope);
61426249
61436250 return Zir.Inst.Ref.unreachable_value;
61446251 },
......@@ -6885,7 +6992,7 @@ fn builtinCall(
68856992 .field => {
68866993 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
68876994 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{
68896996 .lhs = try expr(gz, scope, .ref, params[0]),
68906997 .field_name = field_name,
68916998 });
src/Module.zig+17-2
......@@ -755,6 +755,7 @@ pub const Fn = struct {
755755 rbrace_column: u16,
756756
757757 state: Analysis,
758 is_cold: bool = false,
758759
759760 pub const Analysis = enum {
760761 queued,
......@@ -776,8 +777,19 @@ pub const Fn = struct {
776777 }
777778
778779 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;
781793 }
782794};
783795
......@@ -3453,6 +3465,9 @@ pub fn clearDecl(
34533465 for (decl.dependencies.keys()) |dep| {
34543466 dep.removeDependant(decl);
34553467 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 });
34563471 // We don't recursively perform a deletion here, because during the update,
34573472 // another reference to it may turn up.
34583473 dep.deletion_flag = true;
src/Sema.zig+224-88
......@@ -225,12 +225,10 @@ pub fn analyzeBody(
225225 .float => try sema.zirFloat(block, inst),
226226 .float128 => try sema.zirFloat128(block, inst),
227227 .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),
234232 .loop => try sema.zirLoop(block, inst),
235233 .merge_error_sets => try sema.zirMergeErrorSets(block, inst),
236234 .negate => try sema.zirNegate(block, inst, .sub),
......@@ -244,6 +242,7 @@ pub fn analyzeBody(
244242 .ptr_type => try sema.zirPtrType(block, inst),
245243 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
246244 .ref => try sema.zirRef(block, inst),
245 .ret_err_value_code => try sema.zirRetErrValueCode(block, inst),
247246 .shl => try sema.zirShl(block, inst),
248247 .shr => try sema.zirShr(block, inst),
249248 .slice_end => try sema.zirSliceEnd(block, inst),
......@@ -380,8 +379,9 @@ pub fn analyzeBody(
380379 .condbr => return sema.zirCondbr(block, inst),
381380 .@"break" => return sema.zirBreak(block, inst),
382381 .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),
384383 .ret_node => return sema.zirRetNode(block, inst),
384 .ret_err_value => return sema.zirRetErrValue(block, inst),
385385 .@"unreachable" => return sema.zirUnreachable(block, inst),
386386 .repeat => return sema.zirRepeat(block, inst),
387387 .panic => return sema.zirPanic(block, inst),
......@@ -587,6 +587,19 @@ pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) error{OutOfMemory}!*ir.In
587587 return sema.inst_map.get(@intCast(u32, i)).?;
588588}
589589
590fn 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
590603fn resolveConstString(
591604 sema: *Sema,
592605 block: *Scope.Block,
......@@ -1754,8 +1767,9 @@ fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
17541767fn zirPanic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index {
17551768 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
17561769 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);
17591773}
17601774
17611775fn 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
20282042
20292043fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
20302044 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;
20332049}
20342050
20352051fn 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
20412057fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
20422058 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
20432059 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);
20492061}
20502062
20512063fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
......@@ -2190,21 +2202,27 @@ fn zirCall(
21902202 const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index);
21912203 const args = sema.code.refSlice(extra.end, extra.data.args_len);
21922204
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);
21942214}
21952215
21962216fn analyzeCall(
21972217 sema: *Sema,
21982218 block: *Scope.Block,
2199 zir_func: Zir.Inst.Ref,
2219 func: *ir.Inst,
22002220 func_src: LazySrcLoc,
22012221 call_src: LazySrcLoc,
22022222 modifier: std.builtin.CallOptions.Modifier,
22032223 ensure_result_used: bool,
2204 zir_args: []const Zir.Inst.Ref,
2224 args: []const *ir.Inst,
22052225) InnerError!*ir.Inst {
2206 const func = try sema.resolveInst(zir_func);
2207
22082226 if (func.ty.zigTypeTag() != .Fn)
22092227 return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty});
22102228
......@@ -2221,22 +2239,22 @@ fn analyzeCall(
22212239 const fn_params_len = func.ty.fnParamLen();
22222240 if (func.ty.fnIsVarArgs()) {
22232241 assert(cc == .C);
2224 if (zir_args.len < fn_params_len) {
2242 if (args.len < fn_params_len) {
22252243 // TODO add error note: declared here
22262244 return sema.mod.fail(
22272245 &block.base,
22282246 func_src,
22292247 "expected at least {d} argument(s), found {d}",
2230 .{ fn_params_len, zir_args.len },
2248 .{ fn_params_len, args.len },
22312249 );
22322250 }
2233 } else if (fn_params_len != zir_args.len) {
2251 } else if (fn_params_len != args.len) {
22342252 // TODO add error note: declared here
22352253 return sema.mod.fail(
22362254 &block.base,
22372255 func_src,
22382256 "expected {d} argument(s), found {d}",
2239 .{ fn_params_len, zir_args.len },
2257 .{ fn_params_len, args.len },
22402258 );
22412259 }
22422260
......@@ -2256,13 +2274,6 @@ fn analyzeCall(
22562274 }),
22572275 }
22582276
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
22662277 const ret_type = func.ty.fnReturnType();
22672278
22682279 const is_comptime_call = block.is_comptime or modifier == .compile_time;
......@@ -2323,7 +2334,7 @@ fn analyzeCall(
23232334 defer sema.func = parent_func;
23242335
23252336 const parent_param_inst_list = sema.param_inst_list;
2326 sema.param_inst_list = casted_args;
2337 sema.param_inst_list = args;
23272338 defer sema.param_inst_list = parent_param_inst_list;
23282339
23292340 const parent_next_arg_index = sema.next_arg_index;
......@@ -2357,7 +2368,7 @@ fn analyzeCall(
23572368 break :res result;
23582369 } else res: {
23592370 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);
23612372 };
23622373
23632374 if (ensure_result_used) {
......@@ -2968,17 +2979,19 @@ fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
29682979 if (operand.ty.zigTypeTag() != .ErrorUnion)
29692980 return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});
29702981
2982 const result_ty = operand.ty.castTag(.error_union).?.data.error_set;
2983
29712984 if (operand.value()) |val| {
29722985 assert(val.getError() != null);
29732986 const data = val.castTag(.error_union).?.data;
29742987 return sema.mod.constInst(sema.arena, src, .{
2975 .ty = operand.ty.castTag(.error_union).?.data.error_set,
2988 .ty = result_ty,
29762989 .val = data,
29772990 });
29782991 }
29792992
29802993 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);
29822995}
29832996
29842997/// Pointer in, value out
......@@ -2994,18 +3007,20 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
29943007 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
29953008 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});
29963009
3010 const result_ty = operand.ty.elemType().castTag(.error_union).?.data.error_set;
3011
29973012 if (operand.value()) |pointer_val| {
29983013 const val = try pointer_val.pointerDeref(sema.arena);
29993014 assert(val.getError() != null);
30003015 const data = val.castTag(.error_union).?.data;
30013016 return sema.mod.constInst(sema.arena, src, .{
3002 .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set,
3017 .ty = result_ty,
30033018 .val = data,
30043019 });
30053020 }
30063021
30073022 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);
30093024}
30103025
30113026fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
......@@ -3081,28 +3096,31 @@ fn funcCommon(
30813096) InnerError!*Inst {
30823097 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
30833098 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);
30853100
30863101 const mod = sema.mod;
30873102
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
30883106 const fn_ty: Type = fn_ty: {
30893107 // Hot path for some common function types.
30903108 if (zir_param_types.len == 0 and !var_args and align_val.tag() == .null_value and
30913109 !inferred_error_set)
30923110 {
3093 if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
3111 if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
30943112 break :fn_ty Type.initTag(.fn_noreturn_no_args);
30953113 }
30963114
3097 if (return_type.zigTypeTag() == .Void and cc == .Unspecified) {
3115 if (bare_return_type.zigTypeTag() == .Void and cc == .Unspecified) {
30983116 break :fn_ty Type.initTag(.fn_void_no_args);
30993117 }
31003118
3101 if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) {
3119 if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Naked) {
31023120 break :fn_ty Type.initTag(.fn_naked_noreturn_no_args);
31033121 }
31043122
3105 if (return_type.zigTypeTag() == .Void and cc == .C) {
3123 if (bare_return_type.zigTypeTag() == .Void and cc == .C) {
31063124 break :fn_ty Type.initTag(.fn_ccc_void_no_args);
31073125 }
31083126 }
......@@ -3120,9 +3138,16 @@ fn funcCommon(
31203138 return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{});
31213139 }
31223140
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 };
31263151
31273152 break :fn_ty try Type.Tag.function.create(sema.arena, .{
31283153 .param_types = param_types,
......@@ -3188,7 +3213,6 @@ fn funcCommon(
31883213 const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued;
31893214
31903215 const fn_payload = try sema.arena.create(Value.Payload.Function);
3191 const new_func = try sema.gpa.create(Module.Fn);
31923216 new_func.* = .{
31933217 .state = anal_state,
31943218 .zir_body_inst = body_inst,
......@@ -4542,6 +4566,12 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
45424566 return mod.constType(sema.arena, src, file_root_decl.ty);
45434567}
45444568
4569fn 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
45454575fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
45464576 const tracy = trace(@src());
45474577 defer tracy.end();
......@@ -5273,11 +5303,10 @@ fn zirBoolBr(
52735303 return &block_inst.base;
52745304}
52755305
5276fn zirIsNull(
5306fn zirIsNonNull(
52775307 sema: *Sema,
52785308 block: *Scope.Block,
52795309 inst: Zir.Inst.Index,
5280 invert_logic: bool,
52815310) InnerError!*Inst {
52825311 const tracy = trace(@src());
52835312 defer tracy.end();
......@@ -5285,14 +5314,13 @@ fn zirIsNull(
52855314 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
52865315 const src = inst_data.src();
52875316 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);
52895318}
52905319
5291fn zirIsNullPtr(
5320fn zirIsNonNullPtr(
52925321 sema: *Sema,
52935322 block: *Scope.Block,
52945323 inst: Zir.Inst.Index,
5295 invert_logic: bool,
52965324) InnerError!*Inst {
52975325 const tracy = trace(@src());
52985326 defer tracy.end();
......@@ -5301,19 +5329,19 @@ fn zirIsNullPtr(
53015329 const src = inst_data.src();
53025330 const ptr = try sema.resolveInst(inst_data.operand);
53035331 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);
53055333}
53065334
5307fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5335fn zirIsNonErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
53085336 const tracy = trace(@src());
53095337 defer tracy.end();
53105338
53115339 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
53125340 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);
53145342}
53155343
5316fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
5344fn zirIsNonErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
53175345 const tracy = trace(@src());
53185346 defer tracy.end();
53195347
......@@ -5321,7 +5349,7 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
53215349 const src = inst_data.src();
53225350 const ptr = try sema.resolveInst(inst_data.operand);
53235351 const loaded = try sema.analyzeLoad(block, src, ptr, src);
5324 return sema.analyzeIsErr(block, src, loaded);
5352 return sema.analyzeIsNonErr(block, src, loaded);
53255353}
53265354
53275355fn zirCondbr(
......@@ -5388,7 +5416,31 @@ fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE
53885416 }
53895417}
53905418
5391fn zirRetTok(
5419fn 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
5443fn zirRetCoerce(
53925444 sema: *Sema,
53935445 block: *Scope.Block,
53945446 inst: Zir.Inst.Index,
......@@ -6195,6 +6247,10 @@ fn zirFuncExtended(
61956247 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
61966248 }
61976249
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
61986254 return sema.funcCommon(
61996255 block,
62006256 extra.data.src_node,
......@@ -6203,9 +6259,9 @@ fn zirFuncExtended(
62036259 extra.data.return_type,
62046260 cc,
62056261 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,
62096265 src_locs,
62106266 lib_name,
62116267 );
......@@ -6357,15 +6413,75 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id:
63576413 try parent_block.instructions.append(sema.gpa, &block_inst.base);
63586414}
63596415
6360fn 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);
6416fn 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);
63666444 return always_noreturn;
63676445}
63686446
6447fn 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
63696485fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
63706486 sema.branch_count += 1;
63716487 if (sema.branch_count > sema.branch_quota) {
......@@ -7102,20 +7218,25 @@ fn analyzeIsNull(
71027218 return block.addUnOp(src, result_ty, inst_tag, operand);
71037219}
71047220
7105fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst {
7221fn analyzeIsNonErr(
7222 sema: *Sema,
7223 block: *Scope.Block,
7224 src: LazySrcLoc,
7225 operand: *Inst,
7226) InnerError!*Inst {
71067227 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);
71097230 assert(ot == .ErrorUnion);
71107231 const result_ty = Type.initTag(.bool);
71117232 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| {
71127233 if (err_union.isUndef()) {
71137234 return sema.mod.constUndef(sema.arena, src, result_ty);
71147235 }
7115 return sema.mod.constBool(sema.arena, src, err_union.getError() != null);
7236 return sema.mod.constBool(sema.arena, src, err_union.getError() == null);
71167237 }
71177238 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);
71197240}
71207241
71217242fn analyzeSlice(
......@@ -7377,15 +7498,13 @@ fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst)
73777498}
73787499
73797500fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
7380 // TODO deal with inferred error sets
73817501 const err_union = dest_type.castTag(.error_union).?;
73827502 if (inst.value()) |val| {
7383 const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: {
7503 if (inst.ty.zigTypeTag() != .ErrorSet) {
73847504 _ = try sema.coerce(block, err_union.data.payload, inst, inst.src);
7385 break :blk val;
73867505 } else switch (err_union.data.error_set.tag()) {
7387 .anyerror => val,
7388 .error_set_single => blk: {
7506 .anyerror => {},
7507 .error_set_single => {
73897508 const expected_name = val.castTag(.@"error").?.data.name;
73907509 const n = err_union.data.error_set.castTag(.error_set_single).?.data;
73917510 if (!mem.eql(u8, expected_name, n)) {
......@@ -7396,9 +7515,8 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst
73967515 .{ err_union.data.error_set, inst.ty },
73977516 );
73987517 }
7399 break :blk val;
74007518 },
7401 .error_set => blk: {
7519 .error_set => {
74027520 const expected_name = val.castTag(.@"error").?.data.name;
74037521 const error_set = err_union.data.error_set.castTag(.error_set).?.data;
74047522 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
74157533 .{ err_union.data.error_set, inst.ty },
74167534 );
74177535 }
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 }
74197548 },
74207549 else => unreachable,
7421 };
7550 }
74227551
74237552 return sema.mod.constInst(sema.arena, inst.src, .{
74247553 .ty = dest_type,
74257554 // 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),
74307556 });
74317557 }
74327558
......@@ -7573,12 +7699,12 @@ fn resolveBuiltinTypeFields(
75737699 return sema.resolveTypeFields(block, src, resolved_ty);
75747700}
75757701
7576fn getBuiltinType(
7702fn getBuiltin(
75777703 sema: *Sema,
75787704 block: *Scope.Block,
75797705 src: LazySrcLoc,
75807706 name: []const u8,
7581) InnerError!Type {
7707) InnerError!*ir.Inst {
75827708 const mod = sema.mod;
75837709 const std_pkg = mod.root_pkg.table.get("std").?;
75847710 const std_file = (mod.importPkg(std_pkg) catch unreachable).file;
......@@ -7596,7 +7722,16 @@ fn getBuiltinType(
75967722 builtin_ty.getNamespace().?,
75977723 name,
75987724 );
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
7728fn 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);
76007735 return sema.resolveAirAsType(block, src, ty_inst);
76017736}
76027737
......@@ -7662,6 +7797,7 @@ fn typeHasOnePossibleValue(
76627797 .error_union,
76637798 .error_set,
76647799 .error_set_single,
7800 .error_set_inferred,
76657801 .@"opaque",
76667802 .var_args_param,
76677803 .manyptr_u8,
src/Zir.zig+33-26
......@@ -1,7 +1,7 @@
11//! 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.
33//! 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
55//! machine code, without any memory access into the AST tree token list, node list,
66//! or source bytes. Exceptions include:
77//! * Compile errors, which may need to reach into these data structures to
......@@ -398,26 +398,20 @@ pub const Inst = struct {
398398 /// Return a boolean false if an optional is null. `x != null`
399399 /// Uses the `un_node` field.
400400 is_non_null,
401 /// Return a boolean true if an optional is null. `x == null`
402 /// Uses the `un_node` field.
403 is_null,
404401 /// Return a boolean false if an optional is null. `x.* != null`
405402 /// Uses the `un_node` field.
406403 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
411405 /// 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
414408 /// Uses the `un_node` field.
415 is_err_ptr,
409 is_non_err_ptr,
416410 /// A labeled block of code that loops forever. At the end of the body will have either
417411 /// a `repeat` instruction or a `repeat_inline` instruction.
418412 /// 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.
421415 /// The payload is `Block`.
422416 loop,
423417 /// Sends runtime control flow back to the beginning of the current block.
......@@ -466,6 +460,19 @@ pub const Inst = struct {
466460 /// Uses the `un_tok` union field.
467461 /// The operand needs to get coerced to the function's return type.
468462 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,
469476 /// Create a pointer type that does not have a sentinel, alignment, or bit range specified.
470477 /// Uses the `ptr_type_simple` union field.
471478 ptr_type_simple,
......@@ -1033,11 +1040,9 @@ pub const Inst = struct {
10331040 .float128,
10341041 .int_type,
10351042 .is_non_null,
1036 .is_null,
10371043 .is_non_null_ptr,
1038 .is_null_ptr,
1039 .is_err,
1040 .is_err_ptr,
1044 .is_non_err,
1045 .is_non_err_ptr,
10411046 .mod_rem,
10421047 .mul,
10431048 .mulwrap,
......@@ -1193,6 +1198,7 @@ pub const Inst = struct {
11931198 .@"resume",
11941199 .@"await",
11951200 .await_nosuspend,
1201 .ret_err_value_code,
11961202 .extended,
11971203 => false,
11981204
......@@ -1203,6 +1209,7 @@ pub const Inst = struct {
12031209 .compile_error,
12041210 .ret_node,
12051211 .ret_coerce,
1212 .ret_err_value,
12061213 .@"unreachable",
12071214 .repeat,
12081215 .repeat_inline,
......@@ -1291,11 +1298,9 @@ pub const Inst = struct {
12911298 .float128 = .pl_node,
12921299 .int_type = .int_type,
12931300 .is_non_null = .un_node,
1294 .is_null = .un_node,
12951301 .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,
12991304 .loop = .pl_node,
13001305 .repeat = .node,
13011306 .repeat_inline = .node,
......@@ -1307,6 +1312,8 @@ pub const Inst = struct {
13071312 .ref = .un_tok,
13081313 .ret_node = .un_node,
13091314 .ret_coerce = .un_tok,
1315 .ret_err_value = .str_tok,
1316 .ret_err_value_code = .str_tok,
13101317 .ptr_type_simple = .ptr_type_simple,
13111318 .ptr_type = .ptr_type,
13121319 .slice_start = .pl_node,
......@@ -2840,11 +2847,9 @@ const Writer = struct {
28402847 .err_union_code,
28412848 .err_union_code_ptr,
28422849 .is_non_null,
2843 .is_null,
28442850 .is_non_null_ptr,
2845 .is_null_ptr,
2846 .is_err,
2847 .is_err_ptr,
2851 .is_non_err,
2852 .is_non_err_ptr,
28482853 .typeof,
28492854 .typeof_elem,
28502855 .struct_init_empty,
......@@ -3077,6 +3082,8 @@ const Writer = struct {
30773082 .decl_val,
30783083 .import,
30793084 .arg,
3085 .ret_err_value,
3086 .ret_err_value_code,
30803087 => try self.writeStrTok(stream, inst),
30813088
30823089 .func => try self.writeFunc(stream, inst, false),
src/air.zig+22-12
......@@ -90,8 +90,12 @@ pub const Inst = struct {
9090 is_non_null_ptr,
9191 /// E!T => bool
9292 is_err,
93 /// E!T => bool (inverted logic)
94 is_non_err,
9395 /// *E!T => bool
9496 is_err_ptr,
97 /// *E!T => bool (inverted logic)
98 is_non_err_ptr,
9599 bool_and,
96100 bool_or,
97101 /// Read a value from a pointer.
......@@ -154,7 +158,9 @@ pub const Inst = struct {
154158 .is_null,
155159 .is_null_ptr,
156160 .is_err,
161 .is_non_err,
157162 .is_err_ptr,
163 .is_non_err_ptr,
158164 .ptrtoint,
159165 .floatcast,
160166 .intcast,
......@@ -672,15 +678,15 @@ pub const Body = struct {
672678/// For debugging purposes, prints a function representation to stderr.
673679pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
674680 const allocator = old_module.gpa;
675 var ctx: DumpTzir = .{
681 var ctx: DumpAir = .{
676682 .allocator = allocator,
677683 .arena = std.heap.ArenaAllocator.init(allocator),
678684 .old_module = &old_module,
679685 .module_fn = module_fn,
680686 .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),
684690 };
685691 defer ctx.inst_table.deinit();
686692 defer ctx.partial_inst_table.deinit();
......@@ -695,12 +701,12 @@ pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
695701 .dependency_failure => std.debug.print("(dependency_failure)", .{}),
696702 .success => {
697703 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");
699705 },
700706 }
701707}
702708
703const DumpTzir = struct {
709const DumpAir = struct {
704710 allocator: *std.mem.Allocator,
705711 arena: std.heap.ArenaAllocator,
706712 old_module: *const Module,
......@@ -718,7 +724,7 @@ const DumpTzir = struct {
718724 /// TODO: Improve this code to include a stack of Body and store the instructions
719725 /// in there. Now we are putting all the instructions in a function local table,
720726 /// 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 {
722728 // First pass to pre-populate the table so that we can show even invalid references.
723729 // Must iterate the same order we iterate the second time.
724730 // We also look for constants and put them in the const_table.
......@@ -737,7 +743,7 @@ const DumpTzir = struct {
737743 return dtz.dumpBody(body, writer);
738744 }
739745
740 fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void {
746 fn fetchInstsAndResolveConsts(dtz: *DumpAir, body: Body) error{OutOfMemory}!void {
741747 for (body.instructions) |inst| {
742748 try dtz.inst_table.put(inst, dtz.next_index);
743749 dtz.next_index += 1;
......@@ -759,7 +765,9 @@ const DumpTzir = struct {
759765 .is_null,
760766 .is_null_ptr,
761767 .is_err,
768 .is_non_err,
762769 .is_err_ptr,
770 .is_non_err_ptr,
763771 .ptrtoint,
764772 .floatcast,
765773 .intcast,
......@@ -865,7 +873,7 @@ const DumpTzir = struct {
865873 }
866874 }
867875
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 {
869877 for (body.instructions) |inst| {
870878 const my_index = dtz.next_partial_index;
871879 try dtz.partial_inst_table.put(inst, my_index);
......@@ -888,11 +896,13 @@ const DumpTzir = struct {
888896 .bitcast,
889897 .not,
890898 .is_non_null,
891 .is_null,
892899 .is_non_null_ptr,
900 .is_null,
893901 .is_null_ptr,
894902 .is_err,
895903 .is_err_ptr,
904 .is_non_err,
905 .is_non_err_ptr,
896906 .ptrtoint,
897907 .floatcast,
898908 .intcast,
......@@ -1150,7 +1160,7 @@ const DumpTzir = struct {
11501160 }
11511161 }
11521162
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 {
11541164 if (dtz.partial_inst_table.get(inst)) |operand_index| {
11551165 try writer.print("%{d}", .{operand_index});
11561166 return null;
......@@ -1166,7 +1176,7 @@ const DumpTzir = struct {
11661176 }
11671177 }
11681178
1169 fn findConst(dtz: *DumpTzir, operand: *Inst) !void {
1179 fn findConst(dtz: *DumpAir, operand: *Inst) !void {
11701180 if (operand.tag == .constant) {
11711181 try dtz.const_table.put(operand, dtz.next_const_index);
11721182 dtz.next_const_index += 1;
src/codegen.zig+141-85
......@@ -142,40 +142,52 @@ pub fn generateSymbol(
142142 ),
143143 };
144144 },
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 = {} };
168181 }
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 },
179191 },
180192 .Int => {
181193 // TODO populate .debug_info for the integer
......@@ -847,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
847859 .is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?),
848860 .is_null => return self.genIsNull(inst.castTag(.is_null).?),
849861 .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).?),
850864 .is_err => return self.genIsErr(inst.castTag(.is_err).?),
851865 .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
852866 .load => return self.genLoad(inst.castTag(.load).?),
......@@ -2244,10 +2258,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22442258 try self.register_manager.getReg(reg, null);
22452259 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
22462260 },
2247 .stack_offset => {
2261 .stack_offset => |off| {
22482262 // Here we need to emit instructions like this:
22492263 // 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);
22512265 },
22522266 .ptr_stack_offset => {
22532267 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 {
29602974 return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});
29612975 }
29622976
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
29632987 fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
29642988 // A loop is a setup to be able to jump back to the beginning.
29652989 const start_index = self.code.items.len;
......@@ -3444,9 +3468,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
34443468 },
34453469 }
34463470 },
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 });
34503476 },
34513477 .register => |reg| {
34523478 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
......@@ -3456,6 +3482,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
34563482 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
34573483 },
34583484 .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
34593488 if (stack_offset == off)
34603489 return; // Copy stack variable to itself; nothing to do.
34613490
......@@ -4161,33 +4190,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
41614190 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
41624191 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
41634192 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 }
41854229 }
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 },
41914235 },
41924236 .Int => {
41934237 const info = typed_value.ty.intInfo(self.target.*);
......@@ -4264,27 +4308,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
42644308 var next_stack_offset: u32 = 0;
42654309
42664310 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;
42884344 }
42894345 }
42904346 result.stack_byte_count = next_stack_offset;
src/codegen/c.zig+285-68
......@@ -39,7 +39,12 @@ const BlockData = struct {
3939};
4040
4141pub const CValueMap = std.AutoHashMap(*Inst, CValue);
42pub const TypedefMap = std.HashMap(Type, struct { name: []const u8, rendered: []u8 }, Type.HashContext, std.hash_map.default_max_load_percentage);
42pub 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);
4348
4449fn formatTypeAsCIdentifier(
4550 data: Type,
......@@ -151,14 +156,49 @@ pub const Object = struct {
151156 render_ty = render_ty.elemType();
152157 }
153158
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 }
162202 try w.writeAll(suffix.items);
163203 }
164204};
......@@ -196,35 +236,72 @@ pub const DeclGen = struct {
196236 return writer.print("{d}", .{val.toSignedInt()});
197237 return writer.print("{d}", .{val.toUnsignedInt()});
198238 },
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("}");
218249 },
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 },
222304 },
223 else => |e| return dg.fail(
224 .{ .node_offset = 0 },
225 "TODO: C backend: implement Pointer value {s}",
226 .{@tagName(e)},
227 ),
228305 },
229306 .Array => {
230307 // First try specific tag representations for more efficiency.
......@@ -283,6 +360,12 @@ pub const DeclGen = struct {
283360 const error_type = t.errorUnionSet();
284361 const payload_type = t.errorUnionChild();
285362 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
286369 try writer.writeByte('(');
287370 try dg.renderType(writer, t);
288371 try writer.writeAll("){");
......@@ -329,6 +412,32 @@ pub const DeclGen = struct {
329412 },
330413 }
331414 },
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 },
332441 else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{
333442 @tagName(e),
334443 }),
......@@ -339,6 +448,12 @@ pub const DeclGen = struct {
339448 if (!is_global) {
340449 try w.writeAll("static ");
341450 }
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 }
342457 try dg.renderType(w, dg.decl.ty.fnReturnType());
343458 const decl_name = mem.span(dg.decl.name);
344459 try w.print(" {s}(", .{decl_name});
......@@ -413,7 +528,35 @@ pub const DeclGen = struct {
413528
414529 .Pointer => {
415530 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 });
417560 } else {
418561 try dg.renderType(w, t.elemType());
419562 try w.writeAll(" *");
......@@ -446,13 +589,13 @@ pub const DeclGen = struct {
446589 try dg.renderType(bw, child_type);
447590 try bw.writeAll(" payload; bool is_null; } ");
448591 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)});
450593
451594 const rendered = buffer.toOwnedSlice();
452595 errdefer dg.typedefs.allocator.free(rendered);
453596 const name = rendered[name_index .. rendered.len - 2];
454597
455 try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
598 try dg.typedefs.ensureUnusedCapacity(1);
456599 try w.writeAll(name);
457600 dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
458601 },
......@@ -465,7 +608,11 @@ pub const DeclGen = struct {
465608 return w.writeAll(some.name);
466609 }
467610 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 }
469616
470617 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
471618 defer buffer.deinit();
......@@ -475,13 +622,20 @@ pub const DeclGen = struct {
475622 try dg.renderType(bw, child_type);
476623 try bw.writeAll(" payload; uint16_t error; } ");
477624 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 }
479633
480634 const rendered = buffer.toOwnedSlice();
481635 errdefer dg.typedefs.allocator.free(rendered);
482636 const name = rendered[name_index .. rendered.len - 2];
483637
484 try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
638 try dg.typedefs.ensureUnusedCapacity(1);
485639 try w.writeAll(name);
486640 dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
487641 },
......@@ -514,7 +668,7 @@ pub const DeclGen = struct {
514668 errdefer dg.typedefs.allocator.free(rendered);
515669 const name = rendered[name_start .. rendered.len - 2];
516670
517 try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
671 try dg.typedefs.ensureUnusedCapacity(1);
518672 try w.writeAll(name);
519673 dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
520674 },
......@@ -526,7 +680,28 @@ pub const DeclGen = struct {
526680 try dg.renderType(w, int_tag_ty);
527681 },
528682 .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 },
530705 .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}),
531706 .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}),
532707 .AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}),
......@@ -569,23 +744,27 @@ pub fn genDecl(o: *Object) !void {
569744 .val = o.dg.decl.val,
570745 };
571746 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
580747 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");
583756
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);
586759
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) {
589768 const writer = o.writer();
590769 try writer.writeAll("ZIG_EXTERN_C ");
591770 try o.dg.renderFunctionSignature(writer, true);
......@@ -644,9 +823,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
644823 const is_global = dg.declIsGlobal(tv);
645824 if (is_global) {
646825 try writer.writeAll("ZIG_EXTERN_C ");
826 try dg.renderFunctionSignature(writer, is_global);
827 try dg.fwd_decl.appendSlice(";\n");
647828 }
648 try dg.renderFunctionSignature(writer, is_global);
649 try dg.fwd_decl.appendSlice(";\n");
650829 },
651830 else => {},
652831 }
......@@ -726,8 +905,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
726905 .ref => try genRef(o, inst.castTag(.ref).?),
727906 .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),
728907
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).?, "*", "->", "=="),
731912
732913 .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
733914 .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
......@@ -1213,9 +1394,25 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {
12131394
12141395// *(E!T) -> E NOT *E
12151396fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
1397 if (inst.base.isUnused())
1398 return CValue.none;
1399
12161400 const writer = o.writer();
12171401 const operand = try o.resolveInst(inst.operand);
12181402
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
12191416 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
12201417
12211418 const local = try o.allocLocal(inst.base.ty, .Const);
......@@ -1225,10 +1422,19 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
12251422 try writer.print("){s}error;\n", .{maybe_deref});
12261423 return local;
12271424}
1425
12281426fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
1427 if (inst.base.isUnused())
1428 return CValue.none;
1429
12291430 const writer = o.writer();
12301431 const operand = try o.resolveInst(inst.operand);
12311432
1433 const payload_ty = inst.operand.ty.errorUnionChild();
1434 if (!payload_ty.hasCodeGenBits()) {
1435 return CValue.none;
1436 }
1437
12321438 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
12331439 const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";
12341440
......@@ -1277,15 +1483,26 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
12771483 return local;
12781484}
12791485
1280fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue {
1486fn 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 {
12811493 const writer = o.writer();
1282 const maybe_deref = if (inst.base.tag == .is_err_ptr) "[0]" else "";
12831494 const operand = try o.resolveInst(inst.operand);
1284
12851495 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 }
12891506 return local;
12901507}
12911508
src/codegen/wasm.zig+4-5
......@@ -814,7 +814,8 @@ pub const Context = struct {
814814 .constant => unreachable,
815815 .dbg_stmt => WValue.none,
816816 .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),
818819 .load => self.genLoad(inst.castTag(.load).?),
819820 .loop => self.genLoop(inst.castTag(.loop).?),
820821 .mul => self.genBinOp(inst.castTag(.mul).?, .mul),
......@@ -1278,7 +1279,7 @@ pub const Context = struct {
12781279 return .none;
12791280 }
12801281
1281 fn genIsErr(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
1282 fn genIsErr(self: *Context, inst: *Inst.UnOp, opcode: wasm.Opcode) InnerError!WValue {
12821283 const operand = self.resolveInst(inst.operand);
12831284 const offset = self.code.items.len;
12841285 const writer = self.code.writer();
......@@ -1289,9 +1290,7 @@ pub const Context = struct {
12891290 try writer.writeByte(wasm.opcode(.i32_const));
12901291 try leb.writeILEB128(writer, @as(i32, 0));
12911292
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));
12951294
12961295 return WValue{ .code_offset = offset };
12971296 }
src/link/C.zig+4-6
......@@ -207,7 +207,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
207207 }
208208
209209 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);
211211 defer typedefs.deinit();
212212
213213 // Typedefs, forward decls and non-functions first.
......@@ -217,14 +217,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
217217 if (!decl.has_tv) continue;
218218 const buf = buf: {
219219 if (decl.val.castTag(.function)) |_| {
220 try typedefs.ensureUnusedCapacity(decl.fn_link.c.typedefs.count());
220221 var it = decl.fn_link.c.typedefs.iterator();
221222 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) {
226225 try err_typedef_writer.writeAll(new.value_ptr.rendered);
227 typedefs.putAssumeCapacityNoClobber(new.key_ptr.*, new.value_ptr.name);
228226 }
229227 }
230228 fn_count += 1;
src/link/C/zig.h+6
......@@ -12,6 +12,12 @@
1212#define zig_threadlocal zig_threadlocal_unavailable
1313#endif
1414
15#if __GNUC__
16#define ZIG_COLD __attribute__ ((cold))
17#else
18#define ZIG_COLD
19#endif
20
1521#if __STDC_VERSION__ >= 199901L
1622#define ZIG_RESTRICT restrict
1723#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
25052505 abbrev_base_type,
25062506 DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
25072507 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
25132509 });
25142510 },
25152511 .Int => {
......@@ -2526,8 +2522,23 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
25262522 // DW.AT_name, DW.FORM_string
25272523 try dbg_info_buffer.writer().print("{}\x00", .{ty});
25282524 },
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 },
25292540 else => {
2530 std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});
2541 log.err("TODO implement .debug_info for type '{}'", .{ty});
25312542 try dbg_info_buffer.append(abbrev_pad1);
25322543 },
25332544 }
src/type.zig+82-3
......@@ -58,7 +58,7 @@ pub const Type = extern union {
5858 .bool => return .Bool,
5959 .void => return .Void,
6060 .type => return .Type,
61 .error_set, .error_set_single, .anyerror => return .ErrorSet,
61 .error_set, .error_set_single, .anyerror, .error_set_inferred => return .ErrorSet,
6262 .comptime_int => return .ComptimeInt,
6363 .comptime_float => return .ComptimeFloat,
6464 .noreturn => return .NoReturn,
......@@ -689,7 +689,15 @@ pub const Type = extern union {
689689 .optional_single_mut_pointer,
690690 .optional_single_const_pointer,
691691 .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 },
693701
694702 .int_signed,
695703 .int_unsigned,
......@@ -756,6 +764,7 @@ pub const Type = extern union {
756764 });
757765 },
758766 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
767 .error_set_inferred => return self.copyPayloadShallow(allocator, Payload.ErrorSetInferred),
759768 .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
760769 .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
761770 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
......@@ -1031,6 +1040,10 @@ pub const Type = extern union {
10311040 const error_set = ty.castTag(.error_set).?.data;
10321041 return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name));
10331042 },
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 },
10341047 .error_set_single => {
10351048 const name = ty.castTag(.error_set_single).?.data;
10361049 return writer.print("error{{{s}}}", .{name});
......@@ -1144,6 +1157,7 @@ pub const Type = extern union {
11441157 .anyerror_void_error_union,
11451158 .error_set,
11461159 .error_set_single,
1160 .error_set_inferred,
11471161 .manyptr_u8,
11481162 .manyptr_const_u8,
11491163 .atomic_ordering,
......@@ -1161,6 +1175,9 @@ pub const Type = extern union {
11611175 .@"struct" => {
11621176 // TODO introduce lazy value mechanism
11631177 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);
11641181 for (struct_obj.fields.values()) |value| {
11651182 if (value.ty.hasCodeGenBits())
11661183 return true;
......@@ -1348,6 +1365,7 @@ pub const Type = extern union {
13481365 .error_set_single,
13491366 .anyerror_void_error_union,
13501367 .anyerror,
1368 .error_set_inferred,
13511369 => return 2, // TODO revisit this when we have the concept of the error tag type
13521370
13531371 .array, .array_sentinel => return self.elemType().abiAlignment(target),
......@@ -1580,6 +1598,7 @@ pub const Type = extern union {
15801598 .error_set_single,
15811599 .anyerror_void_error_union,
15821600 .anyerror,
1601 .error_set_inferred,
15831602 => return 2, // TODO revisit this when we have the concept of the error tag type
15841603
15851604 .int_signed, .int_unsigned => {
......@@ -1744,6 +1763,7 @@ pub const Type = extern union {
17441763 .error_set_single,
17451764 .anyerror_void_error_union,
17461765 .anyerror,
1766 .error_set_inferred,
17471767 => return 16, // TODO revisit this when we have the concept of the error tag type
17481768
17491769 .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data,
......@@ -1863,6 +1883,48 @@ pub const Type = extern union {
18631883 };
18641884 }
18651885
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
18661928 pub fn isConstPtr(self: Type) bool {
18671929 return switch (self.tag()) {
18681930 .single_const_pointer,
......@@ -1915,7 +1977,10 @@ pub const Type = extern union {
19151977 /// Asserts that the type is an optional
19161978 pub fn isPtrLikeOptional(self: Type) bool {
19171979 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
19191984 .optional => {
19201985 var buf: Payload.ElemType = undefined;
19211986 const child_type = self.optionalChild(&buf);
......@@ -2400,6 +2465,7 @@ pub const Type = extern union {
24002465 .error_union,
24012466 .error_set,
24022467 .error_set_single,
2468 .error_set_inferred,
24032469 .@"opaque",
24042470 .var_args_param,
24052471 .manyptr_u8,
......@@ -2892,6 +2958,8 @@ pub const Type = extern union {
28922958 anyframe_T,
28932959 error_set,
28942960 error_set_single,
2961 /// The type is the inferred error set of a specific function.
2962 error_set_inferred,
28952963 empty_struct,
28962964 @"opaque",
28972965 @"struct",
......@@ -2989,6 +3057,7 @@ pub const Type = extern union {
29893057 => Payload.Bits,
29903058
29913059 .error_set => Payload.ErrorSet,
3060 .error_set_inferred => Payload.ErrorSetInferred,
29923061
29933062 .array, .vector => Payload.Array,
29943063 .array_sentinel => Payload.ArraySentinel,
......@@ -3081,6 +3150,16 @@ pub const Type = extern union {
30813150 data: *Module.ErrorSet,
30823151 };
30833152
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
30843163 pub const Pointer = struct {
30853164 pub const base_tag = Tag.pointer;
30863165
src/value.zig+22-5
......@@ -483,13 +483,13 @@ pub const Value = extern union {
483483 /// TODO this should become a debug dump() function. In order to print values in a meaningful way
484484 /// we also need access to the type.
485485 pub fn format(
486 self: Value,
486 start_val: Value,
487487 comptime fmt: []const u8,
488488 options: std.fmt.FormatOptions,
489489 out_stream: anytype,
490490 ) !void {
491491 comptime assert(fmt.len == 0);
492 var val = self;
492 var val = start_val;
493493 while (true) switch (val.tag()) {
494494 .u8_type => return out_stream.writeAll("u8"),
495495 .i8_type => return out_stream.writeAll("i8"),
......@@ -598,9 +598,9 @@ pub const Value = extern union {
598598 val = field_ptr.container_ptr;
599599 },
600600 .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)}),
604604 .repeated => {
605605 try out_stream.writeAll("(repeated) ");
606606 val = val.castTag(.repeated).?.data;
......@@ -1336,6 +1336,23 @@ pub const Value = extern union {
13361336 };
13371337 }
13381338
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
13391356 /// Asserts the value is a single-item pointer to an array, or an array,
13401357 /// or an unknown-length pointer, and returns the element value at the index.
13411358 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 {
804804 });
805805 }
806806
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
820827 ctx.h("simple header", linux_x64,
821828 \\export fn start() void{}
822829 ,
test/stage2/wasm.zig-2
......@@ -587,8 +587,6 @@ pub fn addCases(ctx: *TestContext) !void {
587587 }
588588
589589 {
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
592590 var case = ctx.exe("wasm error union part 2", wasi);
593591
594592 case.addCompareOutput(