aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/AstGen.zig397
-rw-r--r--src/Sema.zig180
-rw-r--r--src/zir.zig233
3 files changed, 675 insertions, 135 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig
index 5afc49ca3b..2cd8fc25ba 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -22,6 +22,7 @@ const Scope = Module.Scope;
const GenZir = Scope.GenZir;
const InnerError = Module.InnerError;
const Decl = Module.Decl;
+const LazySrcLoc = Module.LazySrcLoc;
const BuiltinFn = @import("BuiltinFn.zig");
instructions: std.MultiArrayList(zir.Inst) = .{},
@@ -1215,6 +1216,7 @@ fn blockExprStmts(
.negate,
.negate_wrap,
.typeof,
+ .typeof_elem,
.xor,
.optional_type,
.optional_type_from_ptr_elem,
@@ -1243,6 +1245,24 @@ fn blockExprStmts(
.slice_sentinel,
.import,
.typeof_peer,
+ .switch_block,
+ .switch_block_multi,
+ .switch_block_else,
+ .switch_block_else_multi,
+ .switch_block_under,
+ .switch_block_under_multi,
+ .switch_block_ref,
+ .switch_block_ref_multi,
+ .switch_block_ref_else,
+ .switch_block_ref_else_multi,
+ .switch_block_ref_under,
+ .switch_block_ref_under_multi,
+ .switch_capture,
+ .switch_capture_ref,
+ .switch_capture_multi,
+ .switch_capture_multi_ref,
+ .switch_capture_else,
+ .switch_capture_else_ref,
=> break :b false,
// ZIR instructions that are always either `noreturn` or `void`.
@@ -1257,18 +1277,6 @@ fn blockExprStmts(
.break_inline,
.condbr,
.condbr_inline,
- .switch_br,
- .switch_br_multi,
- .switch_br_else,
- .switch_br_else_multi,
- .switch_br_under,
- .switch_br_under_multi,
- .switch_br_ref,
- .switch_br_ref_multi,
- .switch_br_ref_else,
- .switch_br_ref_else_multi,
- .switch_br_ref_under,
- .switch_br_ref_under_multi,
.compile_error,
.ret_node,
.ret_tok,
@@ -1543,7 +1551,7 @@ fn assignOp(
const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
- const lhs_type = try gz.addUnTok(.typeof, lhs, infix_node);
+ const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
const rhs = try expr(gz, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{
@@ -2548,15 +2556,28 @@ fn switchExpr(
rl: ResultLoc,
switch_node: ast.Node.Index,
) InnerError!zir.Inst.Ref {
+ const astgen = parent_gz.astgen;
+ const mod = astgen.mod;
+ const gpa = mod.gpa;
const tree = parent_gz.tree();
const node_datas = tree.nodes.items(.data);
const node_tags = tree.nodes.items(.tag);
+ const main_tokens = tree.nodes.items(.main_token);
const token_tags = tree.tokens.items(.tag);
const operand_node = node_datas[switch_node].lhs;
const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
const case_nodes = tree.extra_data[extra.start..extra.end];
+ // We perform two passes over the AST. This first pass is to collect information
+ // for the following variables, make note of the special prong AST node index,
+ // and bail out with a compile error if there are multiple special prongs present.
var any_payload_is_ref = false;
+ var scalar_cases_len: u32 = 0;
+ var multi_cases_len: u32 = 0;
+ var special_prong: zir.SpecialProng = .none;
+ var special_node: ast.Node.Index = 0;
+ var else_src: ?LazySrcLoc = null;
+ var underscore_src: ?LazySrcLoc = null;
for (case_nodes) |case_node| {
const case = switch (node_tags[case_node]) {
.switch_case_one => tree.switchCaseOne(case_node),
@@ -2568,22 +2589,346 @@ fn switchExpr(
any_payload_is_ref = true;
}
}
+ // Check for else/`_` prong.
+ if (case.ast.values.len == 0) {
+ const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1);
+ if (else_src) |src| {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ scope,
+ case_src,
+ "multiple else prongs in switch expression",
+ .{},
+ );
+ errdefer msg.destroy(gpa);
+ try mod.errNote(scope, src, msg, "previous else prong is here", .{});
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(scope, msg);
+ } else if (underscore_src) |some_underscore| {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ scope,
+ parent_gz.nodeSrcLoc(switch_node),
+ "else and '_' prong in switch expression",
+ .{},
+ );
+ errdefer msg.destroy(gpa);
+ try mod.errNote(scope, case_src, msg, "else prong is here", .{});
+ try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(scope, msg);
+ }
+ special_node = case_node;
+ special_prong = .@"else";
+ else_src = case_src;
+ continue;
+ } else if (case.ast.values.len == 1 and
+ node_tags[case.ast.values[0]] == .identifier and
+ mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
+ {
+ const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1);
+ if (underscore_src) |src| {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ scope,
+ case_src,
+ "multiple '_' prongs in switch expression",
+ .{},
+ );
+ errdefer msg.destroy(gpa);
+ try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(scope, msg);
+ } else if (else_src) |some_else| {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ scope,
+ parent_gz.nodeSrcLoc(switch_node),
+ "else and '_' prong in switch expression",
+ .{},
+ );
+ errdefer msg.destroy(gpa);
+ try mod.errNote(scope, some_else, msg, "else prong is here", .{});
+ try mod.errNote(scope, case_src, msg, "'_' prong is here", .{});
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(scope, msg);
+ }
+ special_node = case_node;
+ special_prong = .under;
+ underscore_src = case_src;
+ continue;
+ }
+
+ if (case.ast.values.len == 1 and
+ getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
+ {
+ scalar_cases_len += 1;
+ } else {
+ multi_cases_len += 1;
+ }
}
- const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{
- .rl = .ref,
- .tag = .switch_br_ref,
- } else .{
- .rl = .none,
- .tag = .switch_br,
+ const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
+ const operand = try expr(parent_gz, scope, operand_rl, operand_node);
+ // We need the type of the operand to use as the result location for all the prong items.
+ const typeof_tag: zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof;
+ const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node);
+ const item_rl: ResultLoc = .{ .ty = operand_ty_inst };
+
+ // Contains the data that goes into the `extra` array for the SwitchBr/SwitchBrMulti.
+ // This is the header as well as the optional else prong body, as well as all the
+ // scalar cases.
+ // At the end we will memcpy this into place.
+ var scalar_cases_payload = std.ArrayListUnmanaged(u32){};
+ defer scalar_cases_payload.deinit(gpa);
+ // Same deal, but this is only the `extra` data for the multi cases.
+ var multi_cases_payload = std.ArrayListUnmanaged(u32){};
+ defer multi_cases_payload.deinit(gpa);
+
+ var block_scope: GenZir = .{
+ .parent = scope,
+ .astgen = astgen,
+ .force_comptime = parent_gz.force_comptime,
+ .instructions = .{},
};
- const operand = try expr(parent_gz, scope, rl_and_tag.rl, operand_node);
+ block_scope.setBreakResultLoc(rl);
+ defer block_scope.instructions.deinit(gpa);
- const result = try parent_gz.addPlNode(.switch_br, switch_node, zir.Inst.SwitchBr{
- .operand = operand,
- .cases_len = 0,
- });
- return rvalue(parent_gz, scope, rl, result, switch_node);
+ // This gets added to the parent block later, after the item expressions.
+ const switch_block = try parent_gz.addBlock(undefined, switch_node);
+
+ // We re-use this same scope for all cases, including the special prong, if any.
+ var case_scope: GenZir = .{
+ .parent = &block_scope.base,
+ .astgen = astgen,
+ .force_comptime = parent_gz.force_comptime,
+ .instructions = .{},
+ };
+ defer case_scope.instructions.deinit(gpa);
+
+ // Do the else/`_` first because it goes first in the payload.
+ var capture_val_scope: Scope.LocalVal = undefined;
+ if (special_node != 0) {
+ const case = switch (node_tags[special_node]) {
+ .switch_case_one => tree.switchCaseOne(special_node),
+ .switch_case => tree.switchCase(special_node),
+ else => unreachable,
+ };
+ const sub_scope = blk: {
+ const payload_token = case.payload_token orelse break :blk &case_scope.base;
+ const ident = if (token_tags[payload_token] == .asterisk)
+ payload_token + 1
+ else
+ payload_token;
+ const is_ptr = ident != payload_token;
+ if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
+ if (is_ptr) {
+ return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{});
+ }
+ break :blk &case_scope.base;
+ }
+ const capture_tag: zir.Inst.Tag = if (is_ptr)
+ .switch_capture_else_ref
+ else
+ .switch_capture_else;
+ const capture = try case_scope.add(.{
+ .tag = capture_tag,
+ .data = .{ .switch_capture = .{
+ .switch_inst = switch_block,
+ .prong_index = undefined,
+ } },
+ });
+ const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token);
+ capture_val_scope = .{
+ .parent = &case_scope.base,
+ .gen_zir = &case_scope,
+ .name = capture_name,
+ .inst = capture,
+ .src = parent_gz.tokSrcLoc(payload_token),
+ };
+ break :blk &capture_val_scope.base;
+ };
+ block_scope.break_count += 1;
+ const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
+ if (!astgen.refIsNoReturn(case_result)) {
+ _ = try case_scope.addBreak(.@"break", switch_block, case_result);
+ }
+ // Documentation for this: `zir.Inst.SwitchBr` and `zir.Inst.SwitchBrMulti`.
+ try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
+ 3 + // operand, scalar_cases_len, else body len
+ @boolToInt(multi_cases_len != 0) +
+ case_scope.instructions.items.len);
+ scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
+ scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
+ if (multi_cases_len != 0) {
+ scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
+ }
+ scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
+ scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
+ } else {
+ // Documentation for this: `zir.Inst.SwitchBr` and `zir.Inst.SwitchBrMulti`.
+ try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
+ 2 + // operand, scalar_cases_len
+ @boolToInt(multi_cases_len != 0));
+ scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
+ scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
+ if (multi_cases_len != 0) {
+ scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
+ }
+ }
+
+ // In this pass we generate all the item and prong expressions except the special case.
+ for (case_nodes) |case_node| {
+ if (case_node == special_node)
+ continue;
+ const case = switch (node_tags[case_node]) {
+ .switch_case_one => tree.switchCaseOne(case_node),
+ .switch_case => tree.switchCase(case_node),
+ else => unreachable,
+ };
+
+ // Reset the scope.
+ case_scope.instructions.shrinkRetainingCapacity(0);
+
+ const is_multi_case = case.ast.values.len != 1 or
+ getRangeNode(node_tags, node_datas, case.ast.values[0]) != null;
+
+ const sub_scope = blk: {
+ const payload_token = case.payload_token orelse break :blk &case_scope.base;
+ const ident = if (token_tags[payload_token] == .asterisk)
+ payload_token + 1
+ else
+ payload_token;
+ const is_ptr = ident != payload_token;
+ if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
+ if (is_ptr) {
+ return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{});
+ }
+ break :blk &case_scope.base;
+ }
+ const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
+ const is_ptr_bits: u2 = @boolToInt(is_ptr);
+ const capture_tag: zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
+ 0b00 => .switch_capture,
+ 0b01 => .switch_capture_ref,
+ 0b10 => .switch_capture_multi,
+ 0b11 => .switch_capture_multi_ref,
+ };
+ const capture_index = if (is_multi_case) multi_cases_len else scalar_cases_len;
+ const capture = try case_scope.add(.{
+ .tag = capture_tag,
+ .data = .{ .switch_capture = .{
+ .switch_inst = switch_block,
+ .prong_index = capture_index,
+ } },
+ });
+ const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token);
+ capture_val_scope = .{
+ .parent = &case_scope.base,
+ .gen_zir = &case_scope,
+ .name = capture_name,
+ .inst = capture,
+ .src = parent_gz.tokSrcLoc(payload_token),
+ };
+ break :blk &capture_val_scope.base;
+ };
+
+ if (is_multi_case) {
+ // items_len, ranges_len, body_len
+ const header_index = multi_cases_payload.items.len;
+ try multi_cases_payload.resize(gpa, multi_cases_payload.items.len + 3);
+
+ // items
+ var items_len: u32 = 0;
+ for (case.ast.values) |item_node| {
+ if (getRangeNode(node_tags, node_datas, item_node) != null) continue;
+ items_len += 1;
+
+ const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
+ try multi_cases_payload.append(gpa, @enumToInt(item_inst));
+ }
+
+ // ranges
+ var ranges_len: u32 = 0;
+ for (case.ast.values) |item_node| {
+ const range = getRangeNode(node_tags, node_datas, item_node) orelse continue;
+ ranges_len += 1;
+
+ const first = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].lhs);
+ const last = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].rhs);
+ try multi_cases_payload.appendSlice(gpa, &[_]u32{
+ @enumToInt(first), @enumToInt(last),
+ });
+ }
+
+ block_scope.break_count += 1;
+ const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
+ if (!astgen.refIsNoReturn(case_result)) {
+ _ = try case_scope.addBreak(.@"break", switch_block, case_result);
+ }
+
+ multi_cases_payload.items[header_index + 0] = items_len;
+ multi_cases_payload.items[header_index + 1] = ranges_len;
+ multi_cases_payload.items[header_index + 2] = @intCast(u32, case_scope.instructions.items.len);
+ try multi_cases_payload.appendSlice(gpa, case_scope.instructions.items);
+ } else {
+ const item_node = case.ast.values[0];
+ const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
+ block_scope.break_count += 1;
+ const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
+ if (!astgen.refIsNoReturn(case_result)) {
+ _ = try case_scope.addBreak(.@"break", switch_block, case_result);
+ }
+ try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
+ 2 + case_scope.instructions.items.len);
+ scalar_cases_payload.appendAssumeCapacity(@enumToInt(item_inst));
+ scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
+ scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
+ }
+ }
+ // Now that the item expressions are generated we can add this.
+ try parent_gz.instructions.append(gpa, switch_block);
+
+ const ref_bit: u4 = @boolToInt(any_payload_is_ref);
+ const multi_bit: u4 = @boolToInt(multi_cases_len != 0);
+ const special_prong_bits: u4 = @enumToInt(special_prong);
+ comptime {
+ assert(@enumToInt(zir.SpecialProng.none) == 0b00);
+ assert(@enumToInt(zir.SpecialProng.@"else") == 0b01);
+ assert(@enumToInt(zir.SpecialProng.under) == 0b10);
+ }
+ const zir_tags = astgen.instructions.items(.tag);
+ zir_tags[switch_block] = switch ((ref_bit << 3) | (special_prong_bits << 1) | multi_bit) {
+ 0b0_00_0 => .switch_block,
+ 0b0_00_1 => .switch_block_multi,
+ 0b0_01_0 => .switch_block_else,
+ 0b0_01_1 => .switch_block_else_multi,
+ 0b0_10_0 => .switch_block_under,
+ 0b0_10_1 => .switch_block_under_multi,
+ 0b1_00_0 => .switch_block_ref,
+ 0b1_00_1 => .switch_block_ref_multi,
+ 0b1_01_0 => .switch_block_ref_else,
+ 0b1_01_1 => .switch_block_ref_else_multi,
+ 0b1_10_0 => .switch_block_ref_under,
+ 0b1_10_1 => .switch_block_ref_under_multi,
+ else => unreachable,
+ };
+ const zir_datas = astgen.instructions.items(.data);
+ zir_datas[switch_block].pl_node.payload_index = @intCast(u32, astgen.extra.items.len);
+ try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
+ scalar_cases_payload.items.len + multi_cases_payload.items.len);
+ astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
+ astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
+ const strat = rl.strategy(&block_scope);
+ assert(strat.tag == .break_operand); // TODO
+ assert(!strat.elide_store_to_block_ptr_instructions); // TODO
+ assert(rl != .ref); // TODO
+ const switch_block_ref = astgen.indexToRef(switch_block);
+ return rvalue(parent_gz, scope, rl, switch_block_ref, switch_node);
}
fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
@@ -3021,7 +3366,7 @@ fn typeOf(
return gz.astgen.mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});
}
if (params.len == 1) {
- const result = try gz.addUnTok(.typeof, try expr(gz, scope, .none, params[0]), node);
+ const result = try gz.addUnNode(.typeof, try expr(gz, scope, .none, params[0]), node);
return rvalue(gz, scope, rl, result, node);
}
const arena = gz.astgen.arena;
diff --git a/src/Sema.zig b/src/Sema.zig
index 4cac97f731..ec6f8ff2a9 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -84,6 +84,15 @@ pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
return sema.resolveType(root_block, .unneeded, zir_inst_ref);
}
+/// Returns only the result from the body that is specified.
+/// Only appropriate to call when it is determined at comptime that this body
+/// has no peers.
+fn resolveBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) InnerError!*Inst {
+ const break_inst = try sema.analyzeBody(block, body);
+ const operand_ref = sema.code.instructions.items(.data)[break_inst].@"break".operand;
+ return sema.resolveInst(operand_ref);
+}
+
/// ZIR instructions which are always `noreturn` return this. This matches the
/// return type of `analyzeBody` so that we can tail call them.
/// Only appropriate to return when the instruction is known to be NoReturn
@@ -229,7 +238,26 @@ pub fn analyzeBody(
.str => try sema.zirStr(block, inst),
.sub => try sema.zirArithmetic(block, inst),
.subwrap => try sema.zirArithmetic(block, inst),
+ .switch_block => try sema.zirSwitchBlock(block, inst, false, .none),
+ .switch_block_multi => try sema.zirSwitchBlockMulti(block, inst, false, .none),
+ .switch_block_else => try sema.zirSwitchBlock(block, inst, false, .@"else"),
+ .switch_block_else_multi => try sema.zirSwitchBlockMulti(block, inst, false, .@"else"),
+ .switch_block_under => try sema.zirSwitchBlock(block, inst, false, .under),
+ .switch_block_under_multi => try sema.zirSwitchBlockMulti(block, inst, false, .under),
+ .switch_block_ref => try sema.zirSwitchBlock(block, inst, true, .none),
+ .switch_block_ref_multi => try sema.zirSwitchBlockMulti(block, inst, true, .none),
+ .switch_block_ref_else => try sema.zirSwitchBlock(block, inst, true, .@"else"),
+ .switch_block_ref_else_multi => try sema.zirSwitchBlockMulti(block, inst, true, .@"else"),
+ .switch_block_ref_under => try sema.zirSwitchBlock(block, inst, true, .under),
+ .switch_block_ref_under_multi => try sema.zirSwitchBlockMulti(block, inst, true, .under),
+ .switch_capture => try sema.zirSwitchCapture(block, inst, false, false),
+ .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
+ .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
+ .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
+ .switch_capture_else => try sema.zirSwitchCaptureElse(block, inst, false),
+ .switch_capture_else_ref => try sema.zirSwitchCaptureElse(block, inst, true),
.typeof => try sema.zirTypeof(block, inst),
+ .typeof_elem => try sema.zirTypeofElem(block, inst),
.typeof_peer => try sema.zirTypeofPeer(block, inst),
.xor => try sema.zirBitwise(block, inst, .xor),
@@ -245,18 +273,6 @@ pub fn analyzeBody(
.ret_tok => return sema.zirRetTok(block, inst, false),
.@"unreachable" => return sema.zirUnreachable(block, inst),
.repeat => return sema.zirRepeat(block, inst),
- .switch_br => return sema.zirSwitchBr(block, inst, false, .none),
- .switch_br_multi => return sema.zirSwitchBrMulti(block, inst, false, .none),
- .switch_br_else => return sema.zirSwitchBr(block, inst, false, .@"else"),
- .switch_br_else_multi => return sema.zirSwitchBrMulti(block, inst, false, .@"else"),
- .switch_br_under => return sema.zirSwitchBr(block, inst, false, .under),
- .switch_br_under_multi => return sema.zirSwitchBrMulti(block, inst, false, .under),
- .switch_br_ref => return sema.zirSwitchBr(block, inst, true, .none),
- .switch_br_ref_multi => return sema.zirSwitchBrMulti(block, inst, true, .none),
- .switch_br_ref_else => return sema.zirSwitchBr(block, inst, true, .@"else"),
- .switch_br_ref_else_multi => return sema.zirSwitchBrMulti(block, inst, true, .@"else"),
- .switch_br_ref_under => return sema.zirSwitchBr(block, inst, true, .under),
- .switch_br_ref_under_multi => return sema.zirSwitchBrMulti(block, inst, true, .under),
// Instructions that we know can *never* be noreturn based solely on
// their tag. We avoid needlessly checking if they are noreturn and
@@ -1034,7 +1050,7 @@ fn analyzeBlockBody(
}
assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);
// Here we depend on the br instruction having been over-allocated (if necessary)
- // inide analyzeBreak so that it can be converted into a br_block_flat instruction.
+ // inside zirBreak so that it can be converted into a br_block_flat instruction.
const br_src = br.base.src;
const br_ty = br.base.ty;
const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br);
@@ -1063,22 +1079,15 @@ fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
_ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
}
-fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
+fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].@"break";
+ const src = sema.src;
const operand = try sema.resolveInst(inst_data.operand);
- return sema.analyzeBreak(block, sema.src, inst_data.block_inst, operand);
-}
+ const zir_block = inst_data.block_inst;
-fn analyzeBreak(
- sema: *Sema,
- start_block: *Scope.Block,
- src: LazySrcLoc,
- zir_block: zir.Inst.Index,
- operand: *Inst,
-) InnerError!zir.Inst.Index {
var block = start_block;
while (true) {
if (block.label) |*label| {
@@ -1103,7 +1112,7 @@ fn analyzeBreak(
try start_block.instructions.append(sema.gpa, &br.base);
try label.merges.results.append(sema.gpa, operand);
try label.merges.br_list.append(sema.gpa, br);
- return always_noreturn;
+ return inst;
}
}
block = block.parent.?;
@@ -2208,15 +2217,38 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne
return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
}
-const SpecialProng = enum { none, @"else", under };
+fn zirSwitchCapture(
+ sema: *Sema,
+ block: *Scope.Block,
+ inst: zir.Inst.Index,
+ is_multi: bool,
+ is_ref: bool,
+) InnerError!*Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ @panic("TODO implement Sema for zirSwitchCapture");
+}
-fn zirSwitchBr(
+fn zirSwitchCaptureElse(
sema: *Sema,
block: *Scope.Block,
inst: zir.Inst.Index,
is_ref: bool,
- special_prong: SpecialProng,
-) InnerError!zir.Inst.Index {
+) InnerError!*Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ @panic("TODO implement Sema for zirSwitchCaptureElse");
+}
+
+fn zirSwitchBlock(
+ sema: *Sema,
+ block: *Scope.Block,
+ inst: zir.Inst.Index,
+ is_ref: bool,
+ special_prong: zir.SpecialProng,
+) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
@@ -2238,17 +2270,18 @@ fn zirSwitchBr(
special_prong,
extra.data.cases_len,
0,
+ inst,
inst_data.src_node,
);
}
-fn zirSwitchBrMulti(
+fn zirSwitchBlockMulti(
sema: *Sema,
block: *Scope.Block,
inst: zir.Inst.Index,
is_ref: bool,
- special_prong: SpecialProng,
-) InnerError!zir.Inst.Index {
+ special_prong: zir.SpecialProng,
+) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
@@ -2270,6 +2303,7 @@ fn zirSwitchBrMulti(
special_prong,
extra.data.scalar_cases_len,
extra.data.multi_cases_len,
+ inst,
inst_data.src_node,
);
}
@@ -2279,11 +2313,12 @@ fn analyzeSwitch(
block: *Scope.Block,
operand: *Inst,
extra_end: usize,
- special_prong: SpecialProng,
+ special_prong: zir.SpecialProng,
scalar_cases_len: usize,
multi_cases_len: usize,
+ switch_inst: zir.Inst.Index,
src_node_offset: i32,
-) InnerError!zir.Inst.Index {
+) InnerError!*Inst {
const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {
.none => .{ .body = &.{}, .end = extra_end },
.under, .@"else" => blk: {
@@ -2584,7 +2619,7 @@ fn analyzeSwitch(
const item = try sema.resolveInst(item_ref);
const item_val = try sema.resolveConstValue(block, item.src, item);
if (operand_val.eql(item_val)) {
- return sema.analyzeBody(block, body);
+ return sema.resolveBody(block, body);
}
}
}
@@ -2605,7 +2640,7 @@ fn analyzeSwitch(
const item = try sema.resolveInst(item_ref);
const item_val = try sema.resolveConstValue(block, item.src, item);
if (operand_val.eql(item_val)) {
- return sema.analyzeBody(block, body);
+ return sema.resolveBody(block, body);
}
}
@@ -2621,26 +2656,59 @@ fn analyzeSwitch(
if (Value.compare(operand_val, .gte, first_tv.val) and
Value.compare(operand_val, .lte, last_tv.val))
{
- return sema.analyzeBody(block, body);
+ return sema.resolveBody(block, body);
}
}
extra_index += body_len;
}
}
- return sema.analyzeBody(block, special.body);
+ return sema.resolveBody(block, special.body);
}
if (scalar_cases_len + multi_cases_len == 0) {
- return sema.analyzeBody(block, special.body);
+ return sema.resolveBody(block, special.body);
}
try sema.requireRuntimeBlock(block, src);
+
+ const block_inst = try sema.arena.create(Inst.Block);
+ block_inst.* = .{
+ .base = .{
+ .tag = Inst.Block.base_tag,
+ .ty = undefined, // Set after analysis.
+ .src = src,
+ },
+ .body = undefined,
+ };
+
+ var child_block: Scope.Block = .{
+ .parent = block,
+ .sema = sema,
+ .src_decl = block.src_decl,
+ .instructions = .{},
+ // TODO @as here is working around a stage1 miscompilation bug :(
+ .label = @as(?Scope.Block.Label, Scope.Block.Label{
+ .zir_block = switch_inst,
+ .merges = .{
+ .results = .{},
+ .br_list = .{},
+ .block_inst = block_inst,
+ },
+ }),
+ .inlining = block.inlining,
+ .is_comptime = block.is_comptime,
+ };
+ const merges = &child_block.label.?.merges;
+ defer child_block.instructions.deinit(sema.gpa);
+ defer merges.results.deinit(sema.gpa);
+ defer merges.br_list.deinit(sema.gpa);
+
// TODO when reworking TZIR memory layout make multi cases get generated as cases,
// not as part of the "else" block.
const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
- var case_block = block.makeSubBlock();
+ var case_block = child_block.makeSubBlock();
defer case_block.instructions.deinit(sema.gpa);
var extra_index: usize = special.end;
@@ -2656,7 +2724,7 @@ fn analyzeSwitch(
case_block.instructions.shrinkRetainingCapacity(0);
const item = try sema.resolveInst(item_ref);
- const item_val = try sema.resolveConstValue(block, item.src, item);
+ const item_val = try sema.resolveConstValue(&case_block, item.src, item);
_ = try sema.analyzeBody(&case_block, body);
@@ -2687,7 +2755,7 @@ fn analyzeSwitch(
for (items) |item_ref| {
const item = try sema.resolveInst(item_ref);
- _ = try sema.resolveConstValue(block, item.src, item);
+ _ = try sema.resolveConstValue(&child_block, item.src, item);
const cmp_ok = try case_block.addBinOp(item.src, bool_ty, .cmp_eq, operand, item);
if (any_ok) |some| {
@@ -2707,8 +2775,8 @@ fn analyzeSwitch(
const item_first = try sema.resolveInst(first_ref);
const item_last = try sema.resolveInst(last_ref);
- _ = try sema.resolveConstValue(block, item_first.src, item_first);
- _ = try sema.resolveConstValue(block, item_last.src, item_last);
+ _ = try sema.resolveConstValue(&child_block, item_first.src, item_first);
+ _ = try sema.resolveConstValue(&child_block, item_last.src, item_last);
const range_src = item_first.src;
@@ -2779,8 +2847,8 @@ fn analyzeSwitch(
.instructions = try sema.arena.dupe(*Inst, &[1]*Inst{&first_condbr.base}),
};
- _ = try block.addSwitchBr(src, operand, cases, final_else_body);
- return always_noreturn;
+ _ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
+ return sema.analyzeBlockBody(block, &child_block, merges);
}
fn validateSwitchItem(
@@ -3261,12 +3329,18 @@ fn zirCmp(
}
fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
- const tracy = trace(@src());
- defer tracy.end();
-
- const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
const operand = try sema.resolveInst(inst_data.operand);
- return sema.mod.constType(sema.arena, inst_data.src(), operand.ty);
+ return sema.mod.constType(sema.arena, src, operand.ty);
+}
+
+fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
+ const operand_ptr = try sema.resolveInst(inst_data.operand);
+ const elem_ty = operand_ptr.ty.elemType();
+ return sema.mod.constType(sema.arena, src, elem_ty);
}
fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -3360,8 +3434,7 @@ fn zirBoolBr(
// comptime-known left-hand side. No need for a block here; the result
// is simply the rhs expression. Here we rely on there only being 1
// break instruction (`break_inline`).
- const break_inst = try sema.analyzeBody(parent_block, body);
- return sema.resolveInst(datas[break_inst].@"break".operand);
+ return sema.resolveBody(parent_block, body);
}
const block_inst = try sema.arena.create(Inst.Block);
@@ -3392,8 +3465,7 @@ fn zirBoolBr(
});
_ = try lhs_block.addBr(src, block_inst, lhs_result);
- const rhs_break_inst = try sema.analyzeBody(rhs_block, body);
- const rhs_result = try sema.resolveInst(datas[rhs_break_inst].@"break".operand);
+ const rhs_result = try sema.resolveBody(rhs_block, body);
_ = try rhs_block.addBr(src, block_inst, rhs_result);
const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };
diff --git a/src/zir.zig b/src/zir.zig
index 744dedf4a7..772cb014b6 100644
--- a/src/zir.zig
+++ b/src/zir.zig
@@ -514,6 +514,9 @@ pub const Inst = struct {
/// Returns the type of a value.
/// Uses the `un_tok` field.
typeof,
+ /// Given a value which is a pointer, returns the element type.
+ /// Uses the `un_node` field.
+ typeof_elem,
/// The builtin `@TypeOf` which returns the type after Peer Type Resolution
/// of one or more params.
/// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`.
@@ -588,32 +591,55 @@ pub const Inst = struct {
/// A switch expression. Uses the `pl_node` union field.
/// AST node is the switch, payload is `SwitchBr`.
/// All prongs of target handled.
- switch_br,
- /// Same as switch_br, except one or more prongs have multiple items.
- switch_br_multi,
- /// Same as switch_br, except has an else prong.
- switch_br_else,
- /// Same as switch_br_else, except one or more prongs have multiple items.
- switch_br_else_multi,
- /// Same as switch_br, except has an underscore prong.
- switch_br_under,
- /// Same as switch_br, except one or more prongs have multiple items.
- switch_br_under_multi,
- /// Same as `switch_br` but the target is a pointer to the value being switched on.
- switch_br_ref,
- /// Same as `switch_br_multi` but the target is a pointer to the value being switched on.
- switch_br_ref_multi,
- /// Same as `switch_br_else` but the target is a pointer to the value being switched on.
- switch_br_ref_else,
- /// Same as `switch_br_else_multi` but the target is a pointer to the
+ switch_block,
+ /// Same as switch_block, except one or more prongs have multiple items.
+ switch_block_multi,
+ /// Same as switch_block, except has an else prong.
+ switch_block_else,
+ /// Same as switch_block_else, except one or more prongs have multiple items.
+ switch_block_else_multi,
+ /// Same as switch_block, except has an underscore prong.
+ switch_block_under,
+ /// Same as switch_block, except one or more prongs have multiple items.
+ switch_block_under_multi,
+ /// Same as `switch_block` but the target is a pointer to the value being switched on.
+ switch_block_ref,
+ /// Same as `switch_block_multi` but the target is a pointer to the value being switched on.
+ switch_block_ref_multi,
+ /// Same as `switch_block_else` but the target is a pointer to the value being switched on.
+ switch_block_ref_else,
+ /// Same as `switch_block_else_multi` but the target is a pointer to the
/// value being switched on.
- switch_br_ref_else_multi,
- /// Same as `switch_br_under` but the target is a pointer to the value
+ switch_block_ref_else_multi,
+ /// Same as `switch_block_under` but the target is a pointer to the value
/// being switched on.
- switch_br_ref_under,
- /// Same as `switch_br_under_multi` but the target is a pointer to
+ switch_block_ref_under,
+ /// Same as `switch_block_under_multi` but the target is a pointer to
/// the value being switched on.
- switch_br_ref_under_multi,
+ switch_block_ref_under_multi,
+ /// Produces the capture value for a switch prong.
+ /// Uses the `switch_capture` field.
+ switch_capture,
+ /// Produces the capture value for a switch prong.
+ /// Result is a pointer to the value.
+ /// Uses the `switch_capture` field.
+ switch_capture_ref,
+ /// Produces the capture value for a switch prong.
+ /// The prong is one of the multi cases.
+ /// Uses the `switch_capture` field.
+ switch_capture_multi,
+ /// Produces the capture value for a switch prong.
+ /// The prong is one of the multi cases.
+ /// Result is a pointer to the value.
+ /// Uses the `switch_capture` field.
+ switch_capture_multi_ref,
+ /// Produces the capture value for the else/'_' switch prong.
+ /// Uses the `switch_capture` field.
+ switch_capture_else,
+ /// Produces the capture value for the else/'_' switch prong.
+ /// Result is a pointer to the value.
+ /// Uses the `switch_capture` field.
+ switch_capture_else_ref,
/// Returns whether the instruction is one of the control flow "noreturn" types.
/// Function calls do not count.
@@ -710,6 +736,7 @@ pub const Inst = struct {
.negate,
.negate_wrap,
.typeof,
+ .typeof_elem,
.xor,
.optional_type,
.optional_type_from_ptr_elem,
@@ -743,6 +770,24 @@ pub const Inst = struct {
.set_eval_branch_quota,
.compile_log,
.elided,
+ .switch_capture,
+ .switch_capture_ref,
+ .switch_capture_multi,
+ .switch_capture_multi_ref,
+ .switch_capture_else,
+ .switch_capture_else_ref,
+ .switch_block,
+ .switch_block_multi,
+ .switch_block_else,
+ .switch_block_else_multi,
+ .switch_block_under,
+ .switch_block_under_multi,
+ .switch_block_ref,
+ .switch_block_ref_multi,
+ .switch_block_ref_else,
+ .switch_block_ref_else_multi,
+ .switch_block_ref_under,
+ .switch_block_ref_under_multi,
=> false,
.@"break",
@@ -756,18 +801,6 @@ pub const Inst = struct {
.@"unreachable",
.repeat,
.repeat_inline,
- .switch_br,
- .switch_br_multi,
- .switch_br_else,
- .switch_br_else_multi,
- .switch_br_under,
- .switch_br_under_multi,
- .switch_br_ref,
- .switch_br_ref_multi,
- .switch_br_ref_else,
- .switch_br_ref_else_multi,
- .switch_br_ref_under,
- .switch_br_ref_under_multi,
=> true,
};
}
@@ -1223,6 +1256,10 @@ pub const Inst = struct {
block_inst: Index,
operand: Ref,
},
+ switch_capture: struct {
+ switch_inst: Index,
+ prong_index: u32,
+ },
// Make sure we don't accidentally add a field to make this union
// bigger than expected. Note that in Debug builds, Zig is allowed
@@ -1394,6 +1431,8 @@ pub const Inst = struct {
};
};
+pub const SpecialProng = enum { none, @"else", under };
+
const Writer = struct {
gpa: *Allocator,
arena: *Allocator,
@@ -1461,12 +1500,13 @@ const Writer = struct {
.is_null_ptr,
.is_err,
.is_err_ptr,
+ .typeof,
+ .typeof_elem,
=> try self.writeUnNode(stream, inst),
.ref,
.ret_tok,
.ret_coerce,
- .typeof,
.ensure_err_payload_void,
=> try self.writeUnTok(stream, inst),
@@ -1542,21 +1582,19 @@ const Writer = struct {
.condbr_inline,
=> try self.writePlNodeCondBr(stream, inst),
- .switch_br,
- .switch_br_else,
- .switch_br_under,
- .switch_br_ref,
- .switch_br_ref_else,
- .switch_br_ref_under,
- => try self.writePlNodeSwitchBr(stream, inst),
-
- .switch_br_multi,
- .switch_br_else_multi,
- .switch_br_under_multi,
- .switch_br_ref_multi,
- .switch_br_ref_else_multi,
- .switch_br_ref_under_multi,
- => try self.writePlNodeSwitchBrMulti(stream, inst),
+ .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none),
+ .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
+ .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under),
+ .switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none),
+ .switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
+ .switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under),
+
+ .switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
+ .switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
+ .switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
+ .switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
+ .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
+ .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
.compile_log,
.typeof_peer,
@@ -1588,10 +1626,19 @@ const Writer = struct {
.fn_type_cc => try self.writeFnTypeCc(stream, inst, false),
.fn_type_var_args => try self.writeFnType(stream, inst, true),
.fn_type_cc_var_args => try self.writeFnTypeCc(stream, inst, true),
+
.@"unreachable" => try self.writeUnreachable(stream, inst),
.enum_literal_small => try self.writeSmallStr(stream, inst),
+ .switch_capture,
+ .switch_capture_ref,
+ .switch_capture_multi,
+ .switch_capture_multi_ref,
+ .switch_capture_else,
+ .switch_capture_else_ref,
+ => try self.writeSwitchCapture(stream, inst),
+
.bitcast,
.bitcast_ref,
.bitcast_result_ptr,
@@ -1763,11 +1810,46 @@ const Writer = struct {
try self.writeSrc(stream, inst_data.src());
}
- fn writePlNodeSwitchBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
+ fn writePlNodeSwitchBr(
+ self: *Writer,
+ stream: anytype,
+ inst: Inst.Index,
+ special_prong: SpecialProng,
+ ) !void {
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
const extra = self.code.extraData(Inst.SwitchBr, inst_data.payload_index);
+ const special: struct {
+ body: []const Inst.Index,
+ end: usize,
+ } = switch (special_prong) {
+ .none => .{ .body = &.{}, .end = extra.end },
+ .under, .@"else" => blk: {
+ const body_len = self.code.extra[extra.end];
+ const extra_body_start = extra.end + 1;
+ break :blk .{
+ .body = self.code.extra[extra_body_start..][0..body_len],
+ .end = extra_body_start + body_len,
+ };
+ },
+ };
+
try self.writeInstRef(stream, extra.data.operand);
- var extra_index: usize = extra.end;
+
+ if (special.body.len != 0) {
+ const prong_name = switch (special_prong) {
+ .@"else" => "else",
+ .under => "_",
+ else => unreachable,
+ };
+ try stream.print(", {s} => {{\n", .{prong_name});
+ self.indent += 2;
+ try self.writeBody(stream, special.body);
+ self.indent -= 2;
+ try stream.writeByteNTimes(' ', self.indent);
+ try stream.writeAll("}");
+ }
+
+ var extra_index: usize = special.end;
{
var scalar_i: usize = 0;
while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
@@ -1792,11 +1874,46 @@ const Writer = struct {
try self.writeSrc(stream, inst_data.src());
}
- fn writePlNodeSwitchBrMulti(self: *Writer, stream: anytype, inst: Inst.Index) !void {
+ fn writePlNodeSwitchBlockMulti(
+ self: *Writer,
+ stream: anytype,
+ inst: Inst.Index,
+ special_prong: SpecialProng,
+ ) !void {
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
const extra = self.code.extraData(Inst.SwitchBrMulti, inst_data.payload_index);
+ const special: struct {
+ body: []const Inst.Index,
+ end: usize,
+ } = switch (special_prong) {
+ .none => .{ .body = &.{}, .end = extra.end },
+ .under, .@"else" => blk: {
+ const body_len = self.code.extra[extra.end];
+ const extra_body_start = extra.end + 1;
+ break :blk .{
+ .body = self.code.extra[extra_body_start..][0..body_len],
+ .end = extra_body_start + body_len,
+ };
+ },
+ };
+
try self.writeInstRef(stream, extra.data.operand);
- var extra_index: usize = extra.end;
+
+ if (special.body.len != 0) {
+ const prong_name = switch (special_prong) {
+ .@"else" => "else",
+ .under => "_",
+ else => unreachable,
+ };
+ try stream.print(", {s} => {{\n", .{prong_name});
+ self.indent += 2;
+ try self.writeBody(stream, special.body);
+ self.indent -= 2;
+ try stream.writeByteNTimes(' ', self.indent);
+ try stream.writeAll("}");
+ }
+
+ var extra_index: usize = special.end;
{
var scalar_i: usize = 0;
while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
@@ -2015,6 +2132,12 @@ const Writer = struct {
try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
}
+ fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Inst.Index) !void {
+ const inst_data = self.code.instructions.items(.data)[inst].switch_capture;
+ try self.writeInstIndex(stream, inst_data.switch_inst);
+ try stream.print(", {d})", .{inst_data.prong_index});
+ }
+
fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void {
var i: usize = @enumToInt(ref);