aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-06-13 08:45:12 -0700
committerGitHub <noreply@github.com>2023-06-13 08:45:12 -0700
commitdf6319418a08b611bae23307ace6688f95bedea2 (patch)
tree6cdf873c6800aebccdd8c03a443f9594acb84729 /src
parent387f9568ad0dabd426d382efb45b9c52a4ccc5bb (diff)
parent42dc7539c5b0a39e9b64c5ad92757945b0ca05ad (diff)
downloadzig-df6319418a08b611bae23307ace6688f95bedea2.tar.gz
zig-df6319418a08b611bae23307ace6688f95bedea2.zip
Merge pull request #15880 from mlugg/feat/better-switch-zir-2
Simplify and compact switch ZIR, and resolve union payload captures with PTR
Diffstat (limited to 'src')
-rw-r--r--src/AstGen.zig153
-rw-r--r--src/Autodoc.zig25
-rw-r--r--src/Module.zig158
-rw-r--r--src/Sema.zig1381
-rw-r--r--src/Zir.zig216
-rw-r--r--src/print_zir.zig73
6 files changed, 1231 insertions, 775 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig
index 17cf2aae64..86bd0fd4f4 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -2610,13 +2610,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.slice_length,
.import,
.switch_block,
- .switch_cond,
- .switch_cond_ref,
- .switch_capture,
- .switch_capture_ref,
- .switch_capture_multi,
- .switch_capture_multi_ref,
- .switch_capture_tag,
+ .switch_block_ref,
.struct_init_empty,
.struct_init,
.struct_init_ref,
@@ -2960,7 +2954,7 @@ fn deferStmt(
try gz.astgen.instructions.append(gz.astgen.gpa, .{
.tag = .extended,
.data = .{ .extended = .{
- .opcode = .errdefer_err_code,
+ .opcode = .value_placeholder,
.small = undefined,
.operand = undefined,
} },
@@ -6715,6 +6709,7 @@ fn switchExpr(
// 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 any_has_tag_capture = false;
var scalar_cases_len: u32 = 0;
var multi_cases_len: u32 = 0;
var inline_cases_len: u32 = 0;
@@ -6725,8 +6720,12 @@ fn switchExpr(
for (case_nodes) |case_node| {
const case = tree.fullSwitchCase(case_node).?;
if (case.payload_token) |payload_token| {
- if (token_tags[payload_token] == .asterisk) {
+ const ident = if (token_tags[payload_token] == .asterisk) blk: {
any_payload_is_ref = true;
+ break :blk payload_token + 1;
+ } else payload_token;
+ if (token_tags[ident + 1] == .comma) {
+ any_has_tag_capture = true;
}
}
// Check for else/`_` prong.
@@ -6835,13 +6834,7 @@ fn switchExpr(
const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
- const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond;
- const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node);
- // Sema expects a dbg_stmt immediately after switch_cond(_ref)
- try emitDbgStmt(parent_gz, operand_lc);
- // We need the type of the operand to use as the result location for all the prong items.
- const cond_ty_inst = try parent_gz.addUnNode(.typeof, cond, operand_node);
- const item_ri: ResultInfo = .{ .rl = .{ .ty = cond_ty_inst } };
+ const item_ri: ResultInfo = .{ .rl = .none };
// This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
// except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
@@ -6860,13 +6853,30 @@ fn switchExpr(
block_scope.instructions_top = GenZir.unstacked_top;
block_scope.setBreakResultInfo(ri);
+ // Sema expects a dbg_stmt immediately before switch_block(_ref)
+ try emitDbgStmt(parent_gz, operand_lc);
// This gets added to the parent block later, after the item expressions.
- const switch_block = try parent_gz.makeBlockInst(.switch_block, switch_node);
+ const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block;
+ const switch_block = try parent_gz.makeBlockInst(switch_tag, switch_node);
// We re-use this same scope for all cases, including the special prong, if any.
var case_scope = parent_gz.makeSubBlock(&block_scope.base);
case_scope.instructions_top = GenZir.unstacked_top;
+ // If any prong has an inline tag capture, allocate a shared dummy instruction for it
+ const tag_inst = if (any_has_tag_capture) tag_inst: {
+ const inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
+ try astgen.instructions.append(astgen.gpa, .{
+ .tag = .extended,
+ .data = .{ .extended = .{
+ .opcode = .value_placeholder,
+ .small = undefined,
+ .operand = undefined,
+ } }, // TODO rename opcode
+ });
+ break :tag_inst inst;
+ } else undefined;
+
// In this pass we generate all the item and prong expressions.
var multi_case_index: u32 = 0;
var scalar_case_index: u32 = 0;
@@ -6880,17 +6890,22 @@ fn switchExpr(
var dbg_var_inst: Zir.Inst.Ref = undefined;
var dbg_var_tag_name: ?u32 = null;
var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
- var capture_inst: Zir.Inst.Index = 0;
- var tag_inst: Zir.Inst.Index = 0;
+ var has_tag_capture = false;
var capture_val_scope: Scope.LocalVal = undefined;
var tag_scope: Scope.LocalVal = undefined;
+
+ var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none;
+
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;
+ capture = if (is_ptr) .by_ref else .by_val;
+
const ident_slice = tree.tokenSlice(ident);
var payload_sub_scope: *Scope = undefined;
if (mem.eql(u8, ident_slice, "_")) {
@@ -6899,53 +6914,18 @@ fn switchExpr(
}
payload_sub_scope = &case_scope.base;
} else {
- if (case_node == special_node) {
- const capture_tag: Zir.Inst.Tag = if (is_ptr)
- .switch_capture_ref
- else
- .switch_capture;
- capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
- try astgen.instructions.append(gpa, .{
- .tag = capture_tag,
- .data = .{
- .switch_capture = .{
- .switch_inst = switch_block,
- // Max int communicates that this is the else/underscore prong.
- .prong_index = std.math.maxInt(u32),
- },
- },
- });
- } else {
- 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_case_index else scalar_case_index;
- capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
- try astgen.instructions.append(gpa, .{
- .tag = capture_tag,
- .data = .{ .switch_capture = .{
- .switch_inst = switch_block,
- .prong_index = capture_index,
- } },
- });
- }
const capture_name = try astgen.identAsString(ident);
try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture);
capture_val_scope = .{
.parent = &case_scope.base,
.gen_zir = &case_scope,
.name = capture_name,
- .inst = indexToRef(capture_inst),
+ .inst = indexToRef(switch_block),
.token_src = payload_token,
.id_cat = .capture,
};
dbg_var_name = capture_name;
- dbg_var_inst = indexToRef(capture_inst);
+ dbg_var_inst = indexToRef(switch_block);
payload_sub_scope = &capture_val_scope.base;
}
@@ -6961,14 +6941,9 @@ fn switchExpr(
}
const tag_name = try astgen.identAsString(tag_token);
try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture");
- tag_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
- try astgen.instructions.append(gpa, .{
- .tag = .switch_capture_tag,
- .data = .{ .un_tok = .{
- .operand = cond,
- .src_tok = case_scope.tokenIndexToRelative(tag_token),
- } },
- });
+
+ assert(any_has_tag_capture);
+ has_tag_capture = true;
tag_scope = .{
.parent = payload_sub_scope,
@@ -7034,8 +7009,6 @@ fn switchExpr(
case_scope.instructions_top = parent_gz.instructions.items.len;
defer case_scope.unstack();
- if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst);
- if (tag_inst != 0) try case_scope.instructions.append(gpa, tag_inst);
try case_scope.addDbgBlockBegin();
if (dbg_var_name) |some| {
try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
@@ -7053,10 +7026,42 @@ fn switchExpr(
}
const case_slice = case_scope.instructionsSlice();
- const body_len = astgen.countBodyLenAfterFixups(case_slice);
+ // Since we use the switch_block instruction itself to refer to the
+ // capture, which will not be added to the child block, we need to
+ // handle ref_table manually, and the same for the inline tag
+ // capture instruction.
+ const refs_len = refs: {
+ var n: usize = 0;
+ var check_inst = switch_block;
+ while (astgen.ref_table.get(check_inst)) |ref_inst| {
+ n += 1;
+ check_inst = ref_inst;
+ }
+ if (has_tag_capture) {
+ check_inst = tag_inst;
+ while (astgen.ref_table.get(check_inst)) |ref_inst| {
+ n += 1;
+ check_inst = ref_inst;
+ }
+ }
+ break :refs n;
+ };
+ const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
try payloads.ensureUnusedCapacity(gpa, body_len);
- const inline_bit = @as(u32, @boolToInt(case.inline_token != null)) << 31;
- payloads.items[body_len_index] = body_len | inline_bit;
+ payloads.items[body_len_index] = @bitCast(u32, Zir.Inst.SwitchBlock.ProngInfo{
+ .body_len = @intCast(u28, body_len),
+ .capture = capture,
+ .is_inline = case.inline_token != null,
+ .has_tag_capture = has_tag_capture,
+ });
+ if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
+ appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
+ }
+ if (has_tag_capture) {
+ if (astgen.ref_table.fetchRemove(tag_inst)) |kv| {
+ appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
+ }
+ }
appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
}
}
@@ -7065,14 +7070,16 @@ fn switchExpr(
try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
@boolToInt(multi_cases_len != 0) +
+ @boolToInt(any_has_tag_capture) +
payloads.items.len - case_table_end);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
- .operand = cond,
+ .operand = raw_operand,
.bits = Zir.Inst.SwitchBlock.Bits{
.has_multi_cases = multi_cases_len != 0,
.has_else = special_prong == .@"else",
.has_under = special_prong == .under,
+ .any_has_tag_capture = any_has_tag_capture,
.scalar_cases_len = @intCast(Zir.Inst.SwitchBlock.Bits.ScalarCasesLen, scalar_cases_len),
},
});
@@ -7081,6 +7088,10 @@ fn switchExpr(
astgen.extra.appendAssumeCapacity(multi_cases_len);
}
+ if (any_has_tag_capture) {
+ astgen.extra.appendAssumeCapacity(tag_inst);
+ }
+
const zir_datas = astgen.instructions.items(.data);
const zir_tags = astgen.instructions.items(.tag);
@@ -7103,7 +7114,7 @@ fn switchExpr(
end_index += 3 + items_len + 2 * ranges_len;
}
- const body_len = @truncate(u31, payloads.items[body_len_index]);
+ const body_len = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, payloads.items[body_len_index]).body_len;
end_index += body_len;
switch (strat.tag) {
diff --git a/src/Autodoc.zig b/src/Autodoc.zig
index 1cdb768311..055b8fd989 100644
--- a/src/Autodoc.zig
+++ b/src/Autodoc.zig
@@ -1993,31 +1993,6 @@ fn walkInstruction(
.expr = .{ .switchIndex = switch_index },
};
},
- .switch_cond => {
- const un_node = data[inst_index].un_node;
- const operand = try self.walkRef(
- file,
- parent_scope,
- parent_src,
- un_node.operand,
- need_type,
- );
- const operand_index = self.exprs.items.len;
- try self.exprs.append(self.arena, operand.expr);
-
- // const ast_index = self.ast_nodes.items.len;
- // const sep = "=" ** 200;
- // log.debug("{s}", .{sep});
- // log.debug("SWITCH COND", .{});
- // log.debug("ast index = {}", .{ast_index});
- // log.debug("ast previous = {}", .{self.ast_nodes.items[ast_index - 1]});
- // log.debug("{s}", .{sep});
-
- return DocData.WalkResult{
- .typeRef = operand.typeRef,
- .expr = .{ .typeOf = operand_index },
- };
- },
.typeof => {
const un_node = data[inst_index].un_node;
diff --git a/src/Module.zig b/src/Module.zig
index 61f39a327a..d60f3919a5 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -2471,12 +2471,23 @@ pub const SrcLoc = struct {
}
} else unreachable;
},
- .node_offset_switch_prong_capture => |node_off| {
+ .node_offset_switch_prong_capture,
+ .node_offset_switch_prong_tag_capture,
+ => |node_off| {
const tree = try src_loc.file_scope.getTree(gpa);
const case_node = src_loc.declRelativeToNodeIndex(node_off);
const case = tree.fullSwitchCase(case_node).?;
- const start_tok = case.payload_token.?;
const token_tags = tree.tokens.items(.tag);
+ const start_tok = switch (src_loc.lazy) {
+ .node_offset_switch_prong_capture => case.payload_token.?,
+ .node_offset_switch_prong_tag_capture => blk: {
+ var tok = case.payload_token.?;
+ if (token_tags[tok] == .asterisk) tok += 1;
+ tok += 2; // skip over comma
+ break :blk tok;
+ },
+ else => unreachable,
+ };
const end_tok = switch (token_tags[start_tok]) {
.asterisk => start_tok + 1,
else => start_tok,
@@ -2957,6 +2968,9 @@ pub const LazySrcLoc = union(enum) {
/// The source location points to the capture of a switch_prong.
/// The Decl is determined contextually.
node_offset_switch_prong_capture: i32,
+ /// The source location points to the tag capture of a switch_prong.
+ /// The Decl is determined contextually.
+ node_offset_switch_prong_tag_capture: i32,
/// The source location points to the align expr of a function type
/// expression, found by taking this AST node index offset from the containing
/// Decl AST node, which points to a function type AST node. Next, navigate to
@@ -3130,6 +3144,7 @@ pub const LazySrcLoc = union(enum) {
.node_offset_switch_special_prong,
.node_offset_switch_range,
.node_offset_switch_prong_capture,
+ .node_offset_switch_prong_tag_capture,
.node_offset_fn_type_align,
.node_offset_fn_type_addrspace,
.node_offset_fn_type_section,
@@ -5867,10 +5882,26 @@ fn lockAndClearFileCompileError(mod: *Module, file: *File) void {
}
pub const SwitchProngSrc = union(enum) {
+ /// The item for a scalar prong.
scalar: u32,
+ /// A given single item for a multi prong.
multi: Multi,
+ /// A given range item for a multi prong.
range: Multi,
+ /// The item for the special prong.
+ special,
+ /// The main capture for a scalar prong.
+ scalar_capture: u32,
+ /// The main capture for a multi prong.
multi_capture: u32,
+ /// The main capture for the special prong.
+ special_capture,
+ /// The tag capture for a scalar prong.
+ scalar_tag_capture: u32,
+ /// The tag capture for a multi prong.
+ multi_tag_capture: u32,
+ /// The tag capture for the special prong.
+ special_tag_capture,
pub const Multi = struct {
prong: u32,
@@ -5886,6 +5917,7 @@ pub const SwitchProngSrc = union(enum) {
mod: *Module,
decl: *Decl,
switch_node_offset: i32,
+ /// Ignored if `prong_src` is not `.range`
range_expand: RangeExpand,
) LazySrcLoc {
@setCold(true);
@@ -5906,63 +5938,97 @@ pub const SwitchProngSrc = union(enum) {
var multi_i: u32 = 0;
var scalar_i: u32 = 0;
- for (case_nodes) |case_node| {
+ const case_node = for (case_nodes) |case_node| {
const case = tree.fullSwitchCase(case_node).?;
- if (case.ast.values.len == 0)
- continue;
- 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]]), "_"))
- {
- continue;
+
+ const is_special = special: {
+ if (case.ast.values.len == 0) break :special true;
+ if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .identifier) {
+ break :special mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_");
+ }
+ break :special false;
+ };
+
+ if (is_special) {
+ switch (prong_src) {
+ .special, .special_capture, .special_tag_capture => break case_node,
+ else => continue,
+ }
}
+
const is_multi = case.ast.values.len != 1 or
node_tags[case.ast.values[0]] == .switch_range;
switch (prong_src) {
- .scalar => |i| if (!is_multi and i == scalar_i) return LazySrcLoc.nodeOffset(
- decl.nodeIndexToRelative(case.ast.values[0]),
- ),
- .multi_capture => |i| if (is_multi and i == multi_i) {
- return LazySrcLoc{ .node_offset_switch_prong_capture = decl.nodeIndexToRelative(case_node) };
- },
- .multi => |s| if (is_multi and s.prong == multi_i) {
- var item_i: u32 = 0;
- for (case.ast.values) |item_node| {
- if (node_tags[item_node] == .switch_range) continue;
-
- if (item_i == s.item) return LazySrcLoc.nodeOffset(
- decl.nodeIndexToRelative(item_node),
- );
- item_i += 1;
- } else unreachable;
- },
- .range => |s| if (is_multi and s.prong == multi_i) {
- var range_i: u32 = 0;
- for (case.ast.values) |range| {
- if (node_tags[range] != .switch_range) continue;
-
- if (range_i == s.item) switch (range_expand) {
- .none => return LazySrcLoc.nodeOffset(
- decl.nodeIndexToRelative(range),
- ),
- .first => return LazySrcLoc.nodeOffset(
- decl.nodeIndexToRelative(node_datas[range].lhs),
- ),
- .last => return LazySrcLoc.nodeOffset(
- decl.nodeIndexToRelative(node_datas[range].rhs),
- ),
- };
- range_i += 1;
- } else unreachable;
- },
+ .scalar,
+ .scalar_capture,
+ .scalar_tag_capture,
+ => |i| if (!is_multi and i == scalar_i) break case_node,
+
+ .multi_capture,
+ .multi_tag_capture,
+ => |i| if (is_multi and i == multi_i) break case_node,
+
+ .multi,
+ .range,
+ => |m| if (is_multi and m.prong == multi_i) break case_node,
+
+ .special,
+ .special_capture,
+ .special_tag_capture,
+ => {},
}
+
if (is_multi) {
multi_i += 1;
} else {
scalar_i += 1;
}
} else unreachable;
+
+ const case = tree.fullSwitchCase(case_node).?;
+
+ switch (prong_src) {
+ .scalar, .special => return LazySrcLoc.nodeOffset(
+ decl.nodeIndexToRelative(case.ast.values[0]),
+ ),
+ .multi => |m| {
+ var item_i: u32 = 0;
+ for (case.ast.values) |item_node| {
+ if (node_tags[item_node] == .switch_range) continue;
+ if (item_i == m.item) return LazySrcLoc.nodeOffset(
+ decl.nodeIndexToRelative(item_node),
+ );
+ item_i += 1;
+ }
+ unreachable;
+ },
+ .range => |m| {
+ var range_i: u32 = 0;
+ for (case.ast.values) |range| {
+ if (node_tags[range] != .switch_range) continue;
+ if (range_i == m.item) switch (range_expand) {
+ .none => return LazySrcLoc.nodeOffset(
+ decl.nodeIndexToRelative(range),
+ ),
+ .first => return LazySrcLoc.nodeOffset(
+ decl.nodeIndexToRelative(node_datas[range].lhs),
+ ),
+ .last => return LazySrcLoc.nodeOffset(
+ decl.nodeIndexToRelative(node_datas[range].rhs),
+ ),
+ };
+ range_i += 1;
+ }
+ unreachable;
+ },
+ .scalar_capture, .multi_capture, .special_capture => {
+ return .{ .node_offset_switch_prong_capture = decl.nodeIndexToRelative(case_node) };
+ },
+ .scalar_tag_capture, .multi_tag_capture, .special_tag_capture => {
+ return .{ .node_offset_switch_prong_tag_capture = decl.nodeIndexToRelative(case_node) };
+ },
+ }
}
};
diff --git a/src/Sema.zig b/src/Sema.zig
index cb69fa92d8..7c4968b0e5 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -277,12 +277,6 @@ pub const Block = struct {
c_import_buf: ?*std.ArrayList(u8) = null,
- /// type of `err` in `else => |err|`
- switch_else_err_ty: ?Type = null,
-
- /// Value for switch_capture in an inline case
- inline_case_capture: Air.Inst.Ref = .none,
-
const ComptimeReason = union(enum) {
c_import: struct {
block: *Block,
@@ -397,7 +391,6 @@ pub const Block = struct {
.want_safety = parent.want_safety,
.float_mode = parent.float_mode,
.c_import_buf = parent.c_import_buf,
- .switch_else_err_ty = parent.switch_else_err_ty,
.error_return_trace_index = parent.error_return_trace_index,
};
}
@@ -1014,14 +1007,8 @@ fn analyzeBodyInner(
.slice_start => try sema.zirSliceStart(block, inst),
.slice_length => try sema.zirSliceLength(block, inst),
.str => try sema.zirStr(block, inst),
- .switch_block => try sema.zirSwitchBlock(block, inst),
- .switch_cond => try sema.zirSwitchCond(block, inst, false),
- .switch_cond_ref => try sema.zirSwitchCond(block, inst, true),
- .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_tag => try sema.zirSwitchCaptureTag(block, inst),
+ .switch_block => try sema.zirSwitchBlock(block, inst, false),
+ .switch_block_ref => try sema.zirSwitchBlock(block, inst, true),
.type_info => try sema.zirTypeInfo(block, inst),
.size_of => try sema.zirSizeOf(block, inst),
.bit_size_of => try sema.zirBitSizeOf(block, inst),
@@ -1225,7 +1212,7 @@ fn analyzeBodyInner(
i += 1;
continue;
},
- .errdefer_err_code => unreachable, // never appears in a body
+ .value_placeholder => unreachable, // never appears in a body
};
},
@@ -2405,6 +2392,34 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
return error.AnalysisFail;
}
+/// Given an ErrorMsg, modify its message and source location to the given values, turning the
+/// original message into a note. Notes on the original message are preserved as further notes.
+/// Reference trace is preserved.
+fn reparentOwnedErrorMsg(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ msg: *Module.ErrorMsg,
+ comptime format: []const u8,
+ args: anytype,
+) !void {
+ const mod = sema.mod;
+ const src_decl = mod.declPtr(block.src_decl);
+ const resolved_src = src.toSrcLoc(src_decl, mod);
+ const msg_str = try std.fmt.allocPrint(mod.gpa, format, args);
+
+ const orig_notes = msg.notes.len;
+ msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1);
+ std.mem.copyBackwards(Module.ErrorMsg, msg.notes[1..], msg.notes[0..orig_notes]);
+ msg.notes[0] = .{
+ .src_loc = msg.src_loc,
+ .msg = msg.msg,
+ };
+
+ msg.src_loc = resolved_src;
+ msg.msg = msg_str;
+}
+
const align_ty = Type.u29;
fn analyzeAsAlign(
@@ -10085,251 +10100,544 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
}
-fn zirSwitchCapture(
+/// Holds common data used when analyzing or resolving switch prong bodies,
+/// including setting up captures.
+const SwitchProngAnalysis = struct {
sema: *Sema,
- block: *Block,
- inst: Zir.Inst.Index,
- is_multi: bool,
- is_ref: bool,
-) CompileError!Air.Inst.Ref {
- const tracy = trace(@src());
- defer tracy.end();
+ /// The block containing the `switch_block` itself.
+ parent_block: *Block,
+ /// The raw switch operand value (*not* the condition). Always defined.
+ operand: Air.Inst.Ref,
+ /// May be `undefined` if no prong has a by-ref capture.
+ operand_ptr: Air.Inst.Ref,
+ /// The switch condition value. For unions, `operand` is the union and `cond` is its tag.
+ cond: Air.Inst.Ref,
+ /// If this switch is on an error set, this is the type to assign to the
+ /// `else` prong. If `null`, the prong should be unreachable.
+ else_error_ty: ?Type,
+ /// The index of the `switch_block` instruction itself.
+ switch_block_inst: Zir.Inst.Index,
+ /// The dummy index into which inline tag captures should be placed. May be
+ /// undefined if no prong has a tag capture.
+ tag_capture_inst: Zir.Inst.Index,
+
+ /// Resolve a switch prong which is determined at comptime to have no peers.
+ /// Uses `resolveBlockBody`. Sets up captures as needed.
+ fn resolveProngComptime(
+ spa: SwitchProngAnalysis,
+ child_block: *Block,
+ prong_type: enum { normal, special },
+ prong_body: []const Zir.Inst.Index,
+ capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
+ /// Must use the `scalar_capture`, `special_capture`, or `multi_capture` union field.
+ raw_capture_src: Module.SwitchProngSrc,
+ /// The set of all values which can reach this prong. May be undefined
+ /// if the prong is special or contains ranges.
+ case_vals: []const Air.Inst.Ref,
+ /// The inline capture of this prong. If this is not an inline prong,
+ /// this is `.none`.
+ inline_case_capture: Air.Inst.Ref,
+ /// Whether this prong has an inline tag capture. If `true`, then
+ /// `inline_case_capture` cannot be `.none`.
+ has_tag_capture: bool,
+ merges: *Block.Merges,
+ ) CompileError!Air.Inst.Ref {
+ const sema = spa.sema;
+ const src = sema.code.instructions.items(.data)[spa.switch_block_inst].pl_node.src();
+
+ if (has_tag_capture) {
+ const tag_ref = try spa.analyzeTagCapture(child_block, raw_capture_src, inline_case_capture);
+ sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
+ }
+ defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
+
+ switch (capture) {
+ .none => {
+ return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
+ },
+
+ .by_val, .by_ref => {
+ const capture_ref = try spa.analyzeCapture(
+ child_block,
+ capture == .by_ref,
+ prong_type == .special,
+ raw_capture_src,
+ case_vals,
+ inline_case_capture,
+ );
- const mod = sema.mod;
- const gpa = sema.gpa;
- const zir_datas = sema.code.instructions.items(.data);
- const capture_info = zir_datas[inst].switch_capture;
- const switch_info = zir_datas[capture_info.switch_inst].pl_node;
- const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);
- const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };
- const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;
- const cond_info = zir_datas[cond_inst].un_node;
- const cond_tag = sema.code.instructions.items(.tag)[cond_inst];
- const operand_is_ref = cond_tag == .switch_cond_ref;
- const operand_ptr = try sema.resolveInst(cond_info.operand);
- const operand_ptr_ty = sema.typeOf(operand_ptr);
- const operand_ty = if (operand_is_ref) operand_ptr_ty.childType(mod) else operand_ptr_ty;
-
- if (block.inline_case_capture != .none) {
- const item_val = sema.resolveConstValue(block, .unneeded, block.inline_case_capture, undefined) catch unreachable;
- const resolved_item_val = try sema.resolveLazyValue(item_val);
- if (operand_ty.zigTypeTag(mod) == .Union) {
- const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(resolved_item_val, mod).?);
- const union_obj = mod.typeToUnion(operand_ty).?;
- const field_ty = union_obj.fields.values()[field_index].ty;
- if (try sema.resolveDefinedValue(block, sema.src, operand_ptr)) |union_val| {
- if (is_ref) {
+ if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) {
+ // This prong should be unreachable!
+ return Air.Inst.Ref.unreachable_value;
+ }
+
+ sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
+ defer assert(sema.inst_map.remove(spa.switch_block_inst));
+
+ return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
+ },
+ }
+ }
+
+ /// Analyze a switch prong which may have peers at runtime.
+ /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed.
+ fn analyzeProngRuntime(
+ spa: SwitchProngAnalysis,
+ case_block: *Block,
+ prong_type: enum { normal, special },
+ prong_body: []const Zir.Inst.Index,
+ capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
+ /// Must use the `scalar`, `special`, or `multi_capture` union field.
+ raw_capture_src: Module.SwitchProngSrc,
+ /// The set of all values which can reach this prong. May be undefined
+ /// if the prong is special or contains ranges.
+ case_vals: []const Air.Inst.Ref,
+ /// The inline capture of this prong. If this is not an inline prong,
+ /// this is `.none`.
+ inline_case_capture: Air.Inst.Ref,
+ /// Whether this prong has an inline tag capture. If `true`, then
+ /// `inline_case_capture` cannot be `.none`.
+ has_tag_capture: bool,
+ ) CompileError!void {
+ const sema = spa.sema;
+
+ if (has_tag_capture) {
+ const tag_ref = try spa.analyzeTagCapture(case_block, raw_capture_src, inline_case_capture);
+ sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
+ }
+ defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
+
+ switch (capture) {
+ .none => {
+ return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
+ },
+
+ .by_val, .by_ref => {
+ const capture_ref = try spa.analyzeCapture(
+ case_block,
+ capture == .by_ref,
+ prong_type == .special,
+ raw_capture_src,
+ case_vals,
+ inline_case_capture,
+ );
+
+ if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) {
+ // No need to analyze any further, the prong is unreachable
+ return;
+ }
+
+ sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
+ defer assert(sema.inst_map.remove(spa.switch_block_inst));
+
+ return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
+ },
+ }
+ }
+
+ fn analyzeTagCapture(
+ spa: SwitchProngAnalysis,
+ block: *Block,
+ raw_capture_src: Module.SwitchProngSrc,
+ inline_case_capture: Air.Inst.Ref,
+ ) CompileError!Air.Inst.Ref {
+ const sema = spa.sema;
+ const mod = sema.mod;
+ const operand_ty = sema.typeOf(spa.operand);
+ if (operand_ty.zigTypeTag(mod) != .Union) {
+ const zir_datas = sema.code.instructions.items(.data);
+ const switch_node_offset = zir_datas[spa.switch_block_inst].pl_node.src_node;
+ const raw_tag_capture_src: Module.SwitchProngSrc = switch (raw_capture_src) {
+ .scalar_capture => |i| .{ .scalar_tag_capture = i },
+ .multi_capture => |i| .{ .multi_tag_capture = i },
+ .special_capture => .special_tag_capture,
+ else => unreachable,
+ };
+ const capture_src = raw_tag_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none);
+ const msg = msg: {
+ const msg = try sema.errMsg(block, capture_src, "cannot capture tag of non-union type '{}'", .{
+ operand_ty.fmt(mod),
+ });
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, operand_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+ }
+ assert(inline_case_capture != .none);
+ return inline_case_capture;
+ }
+
+ fn analyzeCapture(
+ spa: SwitchProngAnalysis,
+ block: *Block,
+ capture_byref: bool,
+ is_special_prong: bool,
+ raw_capture_src: Module.SwitchProngSrc,
+ case_vals: []const Air.Inst.Ref,
+ inline_case_capture: Air.Inst.Ref,
+ ) CompileError!Air.Inst.Ref {
+ const sema = spa.sema;
+ const mod = sema.mod;
+
+ const zir_datas = sema.code.instructions.items(.data);
+ const switch_node_offset = zir_datas[spa.switch_block_inst].pl_node.src_node;
+
+ const operand_ty = sema.typeOf(spa.operand);
+ const operand_ptr_ty = if (capture_byref) sema.typeOf(spa.operand_ptr) else undefined;
+ const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_node_offset };
+
+ if (inline_case_capture != .none) {
+ const item_val = sema.resolveConstValue(block, .unneeded, inline_case_capture, "") catch unreachable;
+ if (operand_ty.zigTypeTag(mod) == .Union) {
+ const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, mod).?);
+ const union_obj = mod.typeToUnion(operand_ty).?;
+ const field_ty = union_obj.fields.values()[field_index].ty;
+ if (capture_byref) {
const ptr_field_ty = try Type.ptr(sema.arena, mod, .{
.pointee_type = field_ty,
.mutable = operand_ptr_ty.ptrIsMutable(mod),
.@"volatile" = operand_ptr_ty.isVolatilePtr(mod),
.@"addrspace" = operand_ptr_ty.ptrAddressSpace(mod),
});
- return sema.addConstant(ptr_field_ty, (try mod.intern(.{ .ptr = .{
- .ty = ptr_field_ty.toIntern(),
- .addr = .{ .field = .{
- .base = union_val.toIntern(),
- .index = field_index,
- } },
- } })).toValue());
+ if (try sema.resolveDefinedValue(block, sema.src, spa.operand_ptr)) |union_ptr| {
+ return sema.addConstant(
+ ptr_field_ty,
+ (try mod.intern(.{ .ptr = .{
+ .ty = ptr_field_ty.toIntern(),
+ .addr = .{ .field = .{
+ .base = union_ptr.toIntern(),
+ .index = field_index,
+ } },
+ } })).toValue(),
+ );
+ }
+ return block.addStructFieldPtr(spa.operand_ptr, field_index, ptr_field_ty);
+ } else {
+ if (try sema.resolveDefinedValue(block, sema.src, spa.operand)) |union_val| {
+ const tag_and_val = mod.intern_pool.indexToKey(union_val.toIntern()).un;
+ return sema.addConstant(field_ty, tag_and_val.val.toValue());
+ }
+ return block.addStructFieldVal(spa.operand, field_index, field_ty);
}
- return sema.addConstant(
- field_ty,
- mod.intern_pool.indexToKey(union_val.toIntern()).un.val.toValue(),
- );
- }
- if (is_ref) {
- const ptr_field_ty = try Type.ptr(sema.arena, mod, .{
- .pointee_type = field_ty,
- .mutable = operand_ptr_ty.ptrIsMutable(mod),
- .@"volatile" = operand_ptr_ty.isVolatilePtr(mod),
- .@"addrspace" = operand_ptr_ty.ptrAddressSpace(mod),
- });
- return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty);
+ } else if (capture_byref) {
+ return sema.addConstantMaybeRef(block, operand_ty, item_val, true);
} else {
- return block.addStructFieldVal(operand_ptr, field_index, field_ty);
+ return inline_case_capture;
}
- } else if (is_ref) {
- return sema.addConstantMaybeRef(block, operand_ty, resolved_item_val, true);
- } else {
- return block.inline_case_capture;
}
- }
- const operand = if (operand_is_ref)
- try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
- else
- operand_ptr;
+ if (is_special_prong) {
+ if (capture_byref) {
+ return spa.operand_ptr;
+ }
- if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {
- // It is the else/`_` prong.
- if (is_ref) {
- return operand_ptr;
+ switch (operand_ty.zigTypeTag(mod)) {
+ .ErrorSet => if (spa.else_error_ty) |ty| {
+ return sema.bitCast(block, ty, spa.operand, operand_src, null);
+ } else {
+ try block.addUnreachable(false);
+ return Air.Inst.Ref.unreachable_value;
+ },
+ else => return spa.operand,
+ }
}
switch (operand_ty.zigTypeTag(mod)) {
- .ErrorSet => if (block.switch_else_err_ty) |some| {
- return sema.bitCast(block, some, operand, operand_src, null);
- } else {
- try block.addUnreachable(false);
- return Air.Inst.Ref.unreachable_value;
- },
- else => return operand,
- }
- }
+ .Union => {
+ const union_obj = mod.typeToUnion(operand_ty).?;
+ const first_item_val = sema.resolveConstValue(block, .unneeded, case_vals[0], "") catch unreachable;
- const items = if (is_multi)
- switch_extra.data.getMultiProng(sema.code, switch_extra.end, capture_info.prong_index).items
- else
- &[_]Zir.Inst.Ref{
- switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index).item,
- };
+ const first_field_index = @intCast(u32, operand_ty.unionTagFieldIndex(first_item_val, mod).?);
+ const first_field = union_obj.fields.values()[first_field_index];
- switch (operand_ty.zigTypeTag(mod)) {
- .Union => {
- const union_obj = mod.typeToUnion(operand_ty).?;
- const first_item = try sema.resolveInst(items[0]);
- // Previous switch validation ensured this will succeed
- const first_item_val = sema.resolveConstValue(block, .unneeded, first_item, "") catch unreachable;
-
- const first_field_index = @intCast(u32, operand_ty.unionTagFieldIndex(first_item_val, mod).?);
- const first_field = union_obj.fields.values()[first_field_index];
-
- for (items[1..], 0..) |item, i| {
- const item_ref = try sema.resolveInst(item);
- // Previous switch validation ensured this will succeed
- const item_val = sema.resolveConstValue(block, .unneeded, item_ref, "") catch unreachable;
-
- const field_index = operand_ty.unionTagFieldIndex(item_val, mod).?;
- const field = union_obj.fields.values()[field_index];
- if (!field.ty.eql(first_field.ty, mod)) {
- const msg = msg: {
- const raw_capture_src = Module.SwitchProngSrc{ .multi_capture = capture_info.prong_index };
- const capture_src = raw_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_info.src_node, .first);
+ const field_tys = try sema.arena.alloc(Type, case_vals.len);
+ for (case_vals, field_tys) |item, *field_ty| {
+ const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable;
+ const field_idx = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, sema.mod).?);
+ field_ty.* = union_obj.fields.values()[field_idx].ty;
+ }
- const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{});
- errdefer msg.destroy(gpa);
+ // Fast path: if all the operands are the same type already, we don't need to hit
+ // PTR! This will also allow us to emit simpler code.
+ const same_types = for (field_tys[1..]) |field_ty| {
+ if (!field_ty.eql(field_tys[0], sema.mod)) break false;
+ } else true;
- const raw_first_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = capture_info.prong_index, .item = 0 } };
- const first_item_src = raw_first_item_src.resolve(mod, mod.declPtr(block.src_decl), switch_info.src_node, .first);
- const raw_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = capture_info.prong_index, .item = 1 + @intCast(u32, i) } };
- const item_src = raw_item_src.resolve(mod, mod.declPtr(block.src_decl), switch_info.src_node, .first);
- try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(mod)});
- try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(mod)});
- break :msg msg;
+ const capture_ty = if (same_types) field_tys[0] else capture_ty: {
+ // We need values to run PTR on, so make a bunch of undef constants.
+ const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
+ for (dummy_captures, field_tys) |*dummy, field_ty| {
+ dummy.* = try sema.addConstUndef(field_ty);
+ }
+
+ const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
+ @memset(case_srcs, .unneeded);
+
+ break :capture_ty sema.resolvePeerTypes(block, .unneeded, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
+ error.NeededSourceLocation => {
+ // This must be a multi-prong so this must be a `multi_capture` src
+ const multi_idx = raw_capture_src.multi_capture;
+ const src_decl_ptr = sema.mod.declPtr(block.src_decl);
+ for (case_srcs, 0..) |*case_src, i| {
+ const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(u32, i) } };
+ case_src.* = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
+ }
+ const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
+ _ = sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err1| switch (err1) {
+ error.AnalysisFail => {
+ const msg = sema.err orelse return error.AnalysisFail;
+ try sema.reparentOwnedErrorMsg(block, capture_src, msg, "capture group with incompatible types", .{});
+ return error.AnalysisFail;
+ },
+ else => |e| return e,
+ };
+ unreachable;
+ },
+ else => |e| return e,
};
- return sema.failWithOwnedErrorMsg(msg);
- }
- }
+ };
- if (is_ref) {
- const field_ty_ptr = try Type.ptr(sema.arena, mod, .{
- .pointee_type = first_field.ty,
- .@"addrspace" = .generic,
- .mutable = operand_ptr_ty.ptrIsMutable(mod),
- });
+ // By-reference captures have some further restrictions which make them easier to emit
+ if (capture_byref) {
+ const operand_ptr_info = operand_ptr_ty.ptrInfo(mod);
+ const capture_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = capture_ty,
+ .@"addrspace" = operand_ptr_info.@"addrspace",
+ .mutable = operand_ptr_info.mutable,
+ .@"volatile" = operand_ptr_info.@"volatile",
+ // TODO: alignment!
+ });
- if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| {
- return sema.addConstant(field_ty_ptr, (try mod.intern(.{ .ptr = .{
- .ty = field_ty_ptr.toIntern(),
- .addr = .{ .field = .{
- .base = op_ptr_val.toIntern(),
- .index = first_field_index,
- } },
- } })).toValue());
+ // By-ref captures of hetereogeneous types are only allowed if each field
+ // pointer type is in-memory coercible to the capture pointer type.
+ if (!same_types) {
+ for (field_tys, 0..) |field_ty, i| {
+ const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = field_ty,
+ .@"addrspace" = operand_ptr_info.@"addrspace",
+ .mutable = operand_ptr_info.mutable,
+ .@"volatile" = operand_ptr_info.@"volatile",
+ // TODO: alignment!
+ });
+ if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
+ const multi_idx = raw_capture_src.multi_capture;
+ const src_decl_ptr = sema.mod.declPtr(block.src_decl);
+ const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
+ const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(u32, i) } };
+ const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
+ const msg = msg: {
+ const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{});
+ errdefer msg.destroy(sema.gpa);
+ try sema.errNote(block, case_src, msg, "pointer type child '{}' cannot cast into resolved pointer type child '{}'", .{
+ field_ty.fmt(sema.mod),
+ capture_ty.fmt(sema.mod),
+ });
+ try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+ }
+ }
+ }
+
+ if (try sema.resolveDefinedValue(block, operand_src, spa.operand_ptr)) |op_ptr_val| {
+ if (op_ptr_val.isUndef(mod)) return sema.addConstUndef(capture_ptr_ty);
+ return sema.addConstant(
+ capture_ptr_ty,
+ (try mod.intern(.{ .ptr = .{
+ .ty = capture_ptr_ty.toIntern(),
+ .addr = .{ .field = .{
+ .base = op_ptr_val.toIntern(),
+ .index = first_field_index,
+ } },
+ } })).toValue(),
+ );
+ }
+
+ try sema.requireRuntimeBlock(block, operand_src, null);
+ return block.addStructFieldPtr(spa.operand_ptr, first_field_index, capture_ptr_ty);
+ }
+
+ if (try sema.resolveDefinedValue(block, operand_src, spa.operand)) |operand_val| {
+ if (operand_val.isUndef(mod)) return sema.addConstUndef(capture_ty);
+ const union_val = mod.intern_pool.indexToKey(operand_val.toIntern()).un;
+ if (union_val.tag.toValue().isUndef(mod)) return sema.addConstUndef(capture_ty);
+ const active_field_idx = @intCast(u32, operand_ty.unionTagFieldIndex(union_val.tag.toValue(), sema.mod).?);
+ const field_ty = union_obj.fields.values()[active_field_idx].ty;
+ const uncoerced = try sema.addConstant(field_ty, union_val.val.toValue());
+ return sema.coerce(block, capture_ty, uncoerced, operand_src);
}
+
try sema.requireRuntimeBlock(block, operand_src, null);
- return block.addStructFieldPtr(operand_ptr, first_field_index, field_ty_ptr);
- }
- if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {
- return sema.addConstant(
- first_field.ty,
- mod.intern_pool.indexToKey(operand_val.toIntern()).un.val.toValue(),
- );
- }
- try sema.requireRuntimeBlock(block, operand_src, null);
- return block.addStructFieldVal(operand, first_field_index, first_field.ty);
- },
- .ErrorSet => {
- if (is_multi) {
- var names: Module.Fn.InferredErrorSet.NameMap = .{};
- try names.ensureUnusedCapacity(sema.arena, items.len);
- for (items) |item| {
- const item_ref = try sema.resolveInst(item);
- // Previous switch validation ensured this will succeed
- const item_val = sema.resolveConstLazyValue(block, .unneeded, item_ref, "") catch unreachable;
- names.putAssumeCapacityNoClobber(item_val.getErrorName(mod).unwrap().?, {});
+ if (same_types) {
+ return block.addStructFieldVal(spa.operand, first_field_index, capture_ty);
}
- const else_error_ty = try mod.errorSetFromUnsortedNames(names.keys());
- return sema.bitCast(block, else_error_ty, operand, operand_src, null);
- } else {
- const item_ref = try sema.resolveInst(items[0]);
- // Previous switch validation ensured this will succeed
- const item_val = sema.resolveConstLazyValue(block, .unneeded, item_ref, "") catch unreachable;
+ // We may have to emit a switch block which coerces the operand to the capture type.
+ // If we can, try to avoid that using in-memory coercions.
+ const first_non_imc = in_mem: {
+ for (field_tys, 0..) |field_ty, i| {
+ if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
+ break :in_mem i;
+ }
+ }
+ // All fields are in-memory coercible to the resolved type!
+ // Just take the first field and bitcast the result.
+ const uncoerced = try block.addStructFieldVal(spa.operand, first_field_index, first_field.ty);
+ return block.addBitCast(capture_ty, uncoerced);
+ };
- const item_ty = try mod.singleErrorSetType(item_val.getErrorName(mod).unwrap().?);
- return sema.bitCast(block, item_ty, operand, operand_src, null);
- }
- },
- else => {
- // In this case the capture value is just the passed-through value of the
- // switch condition.
- if (is_ref) {
- return operand_ptr;
- } else {
- return operand;
- }
- },
- }
-}
+ // By-val capture with heterogeneous types which are not all in-memory coercible to
+ // the resolved capture type. We finally have to fall back to the ugly method.
-fn zirSwitchCaptureTag(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- const mod = sema.mod;
- const zir_datas = sema.code.instructions.items(.data);
- const inst_data = zir_datas[inst].un_tok;
- const src = inst_data.src();
+ // However, let's first track which operands are in-memory coercible. There may well
+ // be several, and we can squash all of these cases into the same switch prong using
+ // a simple bitcast. We'll make this the 'else' prong.
- const switch_tag = sema.code.instructions.items(.tag)[Zir.refToIndex(inst_data.operand).?];
- const is_ref = switch_tag == .switch_cond_ref;
- const cond_data = zir_datas[Zir.refToIndex(inst_data.operand).?].un_node;
- const operand_ptr = try sema.resolveInst(cond_data.operand);
- const operand_ptr_ty = sema.typeOf(operand_ptr);
- const operand_ty = if (is_ref) operand_ptr_ty.childType(mod) else operand_ptr_ty;
+ var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_tys.len);
+ in_mem_coercible.unset(first_non_imc);
+ {
+ const next = first_non_imc + 1;
+ for (field_tys[next..], next..) |field_ty, i| {
+ if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) {
+ in_mem_coercible.unset(i);
+ }
+ }
+ }
- if (operand_ty.zigTypeTag(mod) != .Union) {
- const msg = msg: {
- const msg = try sema.errMsg(block, src, "cannot capture tag of non-union type '{}'", .{
- operand_ty.fmt(mod),
- });
- errdefer msg.destroy(sema.gpa);
- try sema.addDeclaredHereNote(msg, operand_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(msg);
- }
+ const capture_block_inst = try block.addInstAsIndex(.{
+ .tag = .block,
+ .data = .{
+ .ty_pl = .{
+ .ty = try sema.addType(capture_ty),
+ .payload = undefined, // updated below
+ },
+ },
+ });
- return block.inline_case_capture;
-}
+ const prong_count = field_tys.len - in_mem_coercible.count();
+
+ const estimated_extra = prong_count * 6; // 2 for Case, 1 item, probably 3 insts
+ var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra);
+ defer cases_extra.deinit();
+
+ {
+ // Non-bitcast cases
+ var it = in_mem_coercible.iterator(.{ .kind = .unset });
+ while (it.next()) |idx| {
+ var coerce_block = block.makeSubBlock();
+ defer coerce_block.instructions.deinit(sema.gpa);
+
+ const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @intCast(u32, idx), field_tys[idx]);
+ const coerced = sema.coerce(&coerce_block, capture_ty, uncoerced, .unneeded) catch |err| switch (err) {
+ error.NeededSourceLocation => {
+ const multi_idx = raw_capture_src.multi_capture;
+ const src_decl_ptr = sema.mod.declPtr(block.src_decl);
+ const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(u32, idx) } };
+ const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none);
+ _ = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);
+ unreachable;
+ },
+ else => |e| return e,
+ };
+ _ = try coerce_block.addBr(capture_block_inst, coerced);
+
+ try cases_extra.ensureUnusedCapacity(3 + coerce_block.instructions.items.len);
+ cases_extra.appendAssumeCapacity(1); // items_len
+ cases_extra.appendAssumeCapacity(@intCast(u32, coerce_block.instructions.items.len)); // body_len
+ cases_extra.appendAssumeCapacity(@enumToInt(case_vals[idx])); // item
+ cases_extra.appendSliceAssumeCapacity(coerce_block.instructions.items); // body
+ }
+ }
+ const else_body_len = len: {
+ // 'else' prong uses a bitcast
+ var coerce_block = block.makeSubBlock();
+ defer coerce_block.instructions.deinit(sema.gpa);
+
+ const first_imc = in_mem_coercible.findFirstSet().?;
+ const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @intCast(u32, first_imc), field_tys[first_imc]);
+ const coerced = try coerce_block.addBitCast(capture_ty, uncoerced);
+ _ = try coerce_block.addBr(capture_block_inst, coerced);
+
+ try cases_extra.appendSlice(coerce_block.instructions.items);
+ break :len coerce_block.instructions.items.len;
+ };
+
+ try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).Struct.fields.len +
+ cases_extra.items.len +
+ @typeInfo(Air.Block).Struct.fields.len +
+ 1);
+
+ const switch_br_inst = @intCast(u32, sema.air_instructions.len);
+ try sema.air_instructions.append(sema.gpa, .{
+ .tag = .switch_br,
+ .data = .{ .pl_op = .{
+ .operand = spa.cond,
+ .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
+ .cases_len = @intCast(u32, prong_count),
+ .else_body_len = @intCast(u32, else_body_len),
+ }),
+ } },
+ });
+ sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
+
+ // Set up block body
+ sema.air_instructions.items(.data)[capture_block_inst].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{
+ .body_len = 1,
+ });
+ sema.air_extra.appendAssumeCapacity(switch_br_inst);
+
+ return Air.indexToRef(capture_block_inst);
+ },
+ .ErrorSet => {
+ if (capture_byref) {
+ const capture_src = raw_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none);
+ return sema.fail(
+ block,
+ capture_src,
+ "error set cannot be captured by reference",
+ .{},
+ );
+ }
+
+ if (case_vals.len == 1) {
+ const item_val = sema.resolveConstValue(block, .unneeded, case_vals[0], "") catch unreachable;
+ const item_ty = try mod.singleErrorSetType(item_val.getErrorName(mod).unwrap().?);
+ return sema.bitCast(block, item_ty, spa.operand, operand_src, null);
+ }
+
+ var names: Module.Fn.InferredErrorSet.NameMap = .{};
+ try names.ensureUnusedCapacity(sema.arena, case_vals.len);
+ for (case_vals) |err| {
+ const err_val = sema.resolveConstValue(block, .unneeded, err, "") catch unreachable;
+ names.putAssumeCapacityNoClobber(err_val.getErrorName(mod).unwrap().?, {});
+ }
+ const error_ty = try mod.errorSetFromUnsortedNames(names.keys());
+ return sema.bitCast(block, error_ty, spa.operand, operand_src, null);
+ },
+ else => {
+ // In this case the capture value is just the passed-through value
+ // of the switch condition.
+ if (capture_byref) {
+ return spa.operand_ptr;
+ } else {
+ return spa.operand;
+ }
+ },
+ }
+ }
+};
-fn zirSwitchCond(
+fn switchCond(
sema: *Sema,
block: *Block,
- inst: Zir.Inst.Index,
- is_ref: bool,
+ src: LazySrcLoc,
+ operand: Air.Inst.Ref,
) CompileError!Air.Inst.Ref {
const mod = sema.mod;
- const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const src = inst_data.src();
- const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
- const operand_ptr = try sema.resolveInst(inst_data.operand);
- const operand = if (is_ref)
- try sema.analyzeLoad(block, src, operand_ptr, operand_src)
- else
- operand_ptr;
const operand_ty = sema.typeOf(operand);
-
switch (operand_ty.zigTypeTag(mod)) {
.Type,
.Void,
@@ -10386,7 +10694,7 @@ fn zirSwitchCond(
const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, Module.SwitchProngSrc);
-fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -10400,10 +10708,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
- const operand = try sema.resolveInst(extra.data.operand);
- // AstGen guarantees that the instruction immediately following
- // switch_cond(_ref) is a dbg_stmt
- const cond_dbg_node_index = Zir.refToIndex(extra.data.operand).? + 1;
+ const raw_operand: struct { val: Air.Inst.Ref, ptr: Air.Inst.Ref } = blk: {
+ const maybe_ptr = try sema.resolveInst(extra.data.operand);
+ if (operand_is_ref) {
+ const val = try sema.analyzeLoad(block, src, maybe_ptr, operand_src);
+ break :blk .{ .val = val, .ptr = maybe_ptr };
+ } else {
+ break :blk .{ .val = maybe_ptr, .ptr = undefined };
+ }
+ };
+
+ const operand = try sema.switchCond(block, operand_src, raw_operand.val);
+
+ // AstGen guarantees that the instruction immediately preceding
+ // switch_block(_ref) is a dbg_stmt
+ const cond_dbg_node_index = inst - 1;
var header_extra_index: usize = extra.end;
@@ -10414,28 +10733,50 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
break :blk multi_cases_len;
} else 0;
+ const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
+ const tag_capture_inst = sema.code.extra[header_extra_index];
+ header_extra_index += 1;
+ // SwitchProngAnalysis wants inst_map to have space for the tag capture.
+ // Note that the normal capture is referred to via the switch block
+ // index, which there is already necessarily space for.
+ try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst});
+ break :blk tag_capture_inst;
+ } else undefined;
+
+ var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
+ defer case_vals.deinit(gpa);
+
+ const Special = struct {
+ body: []const Zir.Inst.Index,
+ end: usize,
+ capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
+ is_inline: bool,
+ has_tag_capture: bool,
+ };
+
const special_prong = extra.data.bits.specialProng();
- const special: struct { body: []const Zir.Inst.Index, end: usize, is_inline: bool } = switch (special_prong) {
- .none => .{ .body = &.{}, .end = header_extra_index, .is_inline = false },
+ const special: Special = switch (special_prong) {
+ .none => .{
+ .body = &.{},
+ .end = header_extra_index,
+ .capture = .none,
+ .is_inline = false,
+ .has_tag_capture = false,
+ },
.under, .@"else" => blk: {
- const body_len = @truncate(u31, sema.code.extra[header_extra_index]);
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[header_extra_index]);
const extra_body_start = header_extra_index + 1;
break :blk .{
- .body = sema.code.extra[extra_body_start..][0..body_len],
- .end = extra_body_start + body_len,
- .is_inline = sema.code.extra[header_extra_index] >> 31 != 0,
+ .body = sema.code.extra[extra_body_start..][0..info.body_len],
+ .end = extra_body_start + info.body_len,
+ .capture = info.capture,
+ .is_inline = info.is_inline,
+ .has_tag_capture = info.has_tag_capture,
};
},
};
- const maybe_union_ty = blk: {
- const zir_tags = sema.code.instructions.items(.tag);
- const zir_data = sema.code.instructions.items(.data);
- const cond_index = Zir.refToIndex(extra.data.operand).?;
- const raw_operand = sema.resolveInst(zir_data[cond_index].un_node.operand) catch unreachable;
- const target_ty = sema.typeOf(raw_operand);
- break :blk if (zir_tags[cond_index] == .switch_cond_ref) target_ty.childType(mod) else target_ty;
- };
+ const maybe_union_ty = sema.typeOf(raw_operand.val);
const union_originally = maybe_union_ty.zigTypeTag(mod) == .Union;
// Duplicate checking variables later also used for `inline else`.
@@ -10495,18 +10836,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
- extra_index += 1;
- extra_index += body_len;
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
+ extra_index += 1 + info.body_len;
- try sema.validateSwitchItemEnum(
+ case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
block,
seen_enum_fields,
&range_set,
item_ref,
+ operand_ty,
src_node_offset,
.{ .scalar = scalar_i },
- );
+ ));
}
}
{
@@ -10516,20 +10857,22 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
- extra_index += items_len + body_len;
+ extra_index += items_len + info.body_len;
+ try case_vals.ensureUnusedCapacity(gpa, items.len);
for (items, 0..) |item_ref, item_i| {
- try sema.validateSwitchItemEnum(
+ case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
block,
seen_enum_fields,
&range_set,
item_ref,
+ operand_ty,
src_node_offset,
.{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
- );
+ ));
}
try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
@@ -10592,17 +10935,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
- extra_index += 1;
- extra_index += body_len;
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
+ extra_index += 1 + info.body_len;
- try sema.validateSwitchItemError(
+ case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
block,
&seen_errors,
item_ref,
+ operand_ty,
src_node_offset,
.{ .scalar = scalar_i },
- );
+ ));
}
}
{
@@ -10612,19 +10955,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
- extra_index += items_len + body_len;
+ extra_index += items_len + info.body_len;
+ try case_vals.ensureUnusedCapacity(gpa, items.len);
for (items, 0..) |item_ref, item_i| {
- try sema.validateSwitchItemError(
+ case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
block,
&seen_errors,
item_ref,
+ operand_ty,
src_node_offset,
.{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
- );
+ ));
}
try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
@@ -10687,7 +11032,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.dbg_block_end,
.dbg_stmt,
.dbg_var_val,
- .switch_capture,
.ret_type,
.as_node,
.ret_node,
@@ -10732,17 +11076,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
- extra_index += 1;
- extra_index += body_len;
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
+ extra_index += 1 + info.body_len;
- try sema.validateSwitchItem(
+ case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
block,
&range_set,
item_ref,
+ operand_ty,
src_node_offset,
.{ .scalar = scalar_i },
- );
+ ));
}
}
{
@@ -10752,21 +11096,24 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len;
+ try case_vals.ensureUnusedCapacity(gpa, items.len);
for (items, 0..) |item_ref, item_i| {
- try sema.validateSwitchItem(
+ case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
block,
&range_set,
item_ref,
+ operand_ty,
src_node_offset,
.{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
- );
+ ));
}
+ try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len);
var range_i: u32 = 0;
while (range_i < ranges_len) : (range_i += 1) {
const item_first = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
@@ -10774,17 +11121,20 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const item_last = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- try sema.validateSwitchRange(
+ const vals = try sema.validateSwitchRange(
block,
&range_set,
item_first,
item_last,
+ operand_ty,
src_node_offset,
.{ .range = .{ .prong = multi_i, .item = range_i } },
);
+ case_vals.appendAssumeCapacity(vals[0]);
+ case_vals.appendAssumeCapacity(vals[1]);
}
- extra_index += body_len;
+ extra_index += info.body_len;
}
}
@@ -10821,18 +11171,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
- extra_index += 1;
- extra_index += body_len;
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
+ extra_index += 1 + info.body_len;
- try sema.validateSwitchItemBool(
+ case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
block,
&true_count,
&false_count,
item_ref,
src_node_offset,
.{ .scalar = scalar_i },
- );
+ ));
}
}
{
@@ -10842,20 +11191,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
- extra_index += items_len + body_len;
+ extra_index += items_len + info.body_len;
+ try case_vals.ensureUnusedCapacity(gpa, items.len);
for (items, 0..) |item_ref, item_i| {
- try sema.validateSwitchItemBool(
+ case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
block,
&true_count,
&false_count,
item_ref,
src_node_offset,
.{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
- );
+ ));
}
try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
@@ -10903,17 +11253,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
extra_index += 1;
- extra_index += body_len;
+ extra_index += info.body_len;
- try sema.validateSwitchItemSparse(
+ case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
block,
&seen_values,
item_ref,
+ operand_ty,
src_node_offset,
.{ .scalar = scalar_i },
- );
+ ));
}
}
{
@@ -10923,19 +11274,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
- extra_index += items_len + body_len;
+ extra_index += items_len + info.body_len;
+ try case_vals.ensureUnusedCapacity(gpa, items.len);
for (items, 0..) |item_ref, item_i| {
- try sema.validateSwitchItemSparse(
+ case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
block,
&seen_values,
item_ref,
+ operand_ty,
src_node_offset,
.{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
- );
+ ));
}
try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
@@ -10961,6 +11314,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}),
}
+ const spa: SwitchProngAnalysis = .{
+ .sema = sema,
+ .parent_block = block,
+ .operand = raw_operand.val,
+ .operand_ptr = raw_operand.ptr,
+ .cond = operand,
+ .else_error_ty = else_error_ty,
+ .switch_block_inst = inst,
+ .tag_capture_inst = tag_capture_inst,
+ };
+
const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
try sema.air_instructions.append(gpa, .{
.tag = .block,
@@ -10988,7 +11352,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.is_comptime = block.is_comptime,
.comptime_reason = block.comptime_reason,
.is_typeof = block.is_typeof,
- .switch_else_err_ty = else_error_ty,
.c_import_buf = block.c_import_buf,
.runtime_cond = block.runtime_cond,
.runtime_loop = block.runtime_loop,
@@ -11005,79 +11368,110 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
{
var scalar_i: usize = 0;
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
- const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
- const is_inline = sema.code.extra[extra_index] >> 31 != 0;
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
extra_index += 1;
- const body = sema.code.extra[extra_index..][0..body_len];
- extra_index += body_len;
-
- const item = try sema.resolveInst(item_ref);
- // Validation above ensured these will succeed.
- const item_val = sema.resolveConstLazyValue(&child_block, .unneeded, item, "") catch unreachable;
- if (resolved_operand_val.eql(item_val, operand_ty, mod)) {
- if (is_inline) child_block.inline_case_capture = operand;
+ const body = sema.code.extra[extra_index..][0..info.body_len];
+ extra_index += info.body_len;
+ const item = case_vals.items[scalar_i];
+ const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable;
+ if (operand_val.eql(item_val, operand_ty, sema.mod)) {
if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
- return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
+ return spa.resolveProngComptime(
+ &child_block,
+ .normal,
+ body,
+ info.capture,
+ .{ .scalar_capture = @intCast(u32, scalar_i) },
+ &.{item},
+ if (info.is_inline) operand else .none,
+ info.has_tag_capture,
+ merges,
+ );
}
}
}
{
var multi_i: usize = 0;
+ var case_val_idx: usize = scalar_cases_len;
while (multi_i < multi_cases_len) : (multi_i += 1) {
const items_len = sema.code.extra[extra_index];
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
- const is_inline = sema.code.extra[extra_index] >> 31 != 0;
- extra_index += 1;
- const items = sema.code.refSlice(extra_index, items_len);
- extra_index += items_len;
- const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len];
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
+ extra_index += 1 + items_len;
+ const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..info.body_len];
- for (items) |item_ref| {
- const item = try sema.resolveInst(item_ref);
- // Validation above ensured these will succeed.
- const item_val = sema.resolveConstLazyValue(&child_block, .unneeded, item, "") catch unreachable;
- if (resolved_operand_val.eql(item_val, operand_ty, mod)) {
- if (is_inline) child_block.inline_case_capture = operand;
+ const items = case_vals.items[case_val_idx..][0..items_len];
+ case_val_idx += items_len;
+ for (items) |item| {
+ // Validation above ensured these will succeed.
+ const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable;
+ if (operand_val.eql(item_val, operand_ty, sema.mod)) {
if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
- return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
+ return spa.resolveProngComptime(
+ &child_block,
+ .normal,
+ body,
+ info.capture,
+ .{ .multi_capture = @intCast(u32, multi_i) },
+ items,
+ if (info.is_inline) operand else .none,
+ info.has_tag_capture,
+ merges,
+ );
}
}
var range_i: usize = 0;
while (range_i < ranges_len) : (range_i += 1) {
- const item_first = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
- extra_index += 1;
- const item_last = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
- extra_index += 1;
+ const range_items = case_vals.items[case_val_idx..][0..2];
+ extra_index += 2;
+ case_val_idx += 2;
// Validation above ensured these will succeed.
- const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first, "") catch unreachable;
- const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last, "") catch unreachable;
- if ((try sema.compareAll(resolved_operand_val, .gte, first_tv.val, operand_ty)) and
- (try sema.compareAll(resolved_operand_val, .lte, last_tv.val, operand_ty)))
+ const first_val = sema.resolveConstValue(&child_block, .unneeded, range_items[0], "") catch unreachable;
+ const last_val = sema.resolveConstValue(&child_block, .unneeded, range_items[1], "") catch unreachable;
+ if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and
+ (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty)))
{
- if (is_inline) child_block.inline_case_capture = operand;
if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
- return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
+ return spa.resolveProngComptime(
+ &child_block,
+ .normal,
+ body,
+ info.capture,
+ .{ .multi_capture = @intCast(u32, multi_i) },
+ undefined, // case_vals may be undefined for ranges
+ if (info.is_inline) operand else .none,
+ info.has_tag_capture,
+ merges,
+ );
}
}
- extra_index += body_len;
+ extra_index += info.body_len;
}
}
if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, special.body, operand);
- if (special.is_inline) child_block.inline_case_capture = operand;
if (empty_enum) {
return Air.Inst.Ref.void_value;
}
- return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
+
+ return spa.resolveProngComptime(
+ &child_block,
+ .special,
+ special.body,
+ special.capture,
+ .special_capture,
+ undefined, // case_vals may be undefined for special prongs
+ if (special.is_inline) operand else .none,
+ special.has_tag_capture,
+ merges,
+ );
}
if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
@@ -11097,7 +11491,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const ok = try block.addUnOp(.is_named_enum_value, operand);
try sema.addSafetyCheck(block, ok, .corrupt_switch);
}
- return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
+
+ return spa.resolveProngComptime(
+ &child_block,
+ .special,
+ special.body,
+ special.capture,
+ .special_capture,
+ undefined, // case_vals may be undefined for special prongs
+ .none,
+ false,
+ merges,
+ );
}
if (child_block.is_comptime) {
@@ -11123,23 +11528,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
var scalar_i: usize = 0;
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
- const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
- const is_inline = sema.code.extra[extra_index] >> 31 != 0;
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
extra_index += 1;
- const body = sema.code.extra[extra_index..][0..body_len];
- extra_index += body_len;
+ const body = sema.code.extra[extra_index..][0..info.body_len];
+ extra_index += info.body_len;
var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope);
defer wip_captures.deinit();
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = wip_captures.scope;
- case_block.inline_case_capture = .none;
- const item = try sema.resolveInst(item_ref);
- if (is_inline) case_block.inline_case_capture = item;
+ const item = case_vals.items[scalar_i];
// `item` is already guaranteed to be constant known.
const analyze_body = if (union_originally) blk: {
@@ -11151,7 +11552,16 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
// nothing to do here
} else if (analyze_body) {
- try sema.analyzeBodyRuntimeBreak(&case_block, body);
+ try spa.analyzeProngRuntime(
+ &case_block,
+ .normal,
+ body,
+ info.capture,
+ .{ .scalar_capture = @intCast(u32, scalar_i) },
+ &.{item},
+ if (info.is_inline) item else .none,
+ info.has_tag_capture,
+ );
} else {
_ = try case_block.addNoOp(.unreach);
}
@@ -11173,38 +11583,38 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
defer gpa.free(prev_then_body);
var cases_len = scalar_cases_len;
+ var case_val_idx: usize = scalar_cases_len;
var multi_i: u32 = 0;
while (multi_i < multi_cases_len) : (multi_i += 1) {
const items_len = sema.code.extra[extra_index];
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = @truncate(u31, sema.code.extra[extra_index]);
- const is_inline = sema.code.extra[extra_index] >> 31 != 0;
- extra_index += 1;
- const items = sema.code.refSlice(extra_index, items_len);
- extra_index += items_len;
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[extra_index]);
+ extra_index += 1 + items_len;
+
+ const items = case_vals.items[case_val_idx..][0..items_len];
+ case_val_idx += items_len;
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = child_block.wip_capture_scope;
- case_block.inline_case_capture = .none;
// Generate all possible cases as scalar prongs.
- if (is_inline) {
+ if (info.is_inline) {
const body_start = extra_index + 2 * ranges_len;
- const body = sema.code.extra[body_start..][0..body_len];
+ const body = sema.code.extra[body_start..][0..info.body_len];
var emit_bb = false;
var range_i: u32 = 0;
while (range_i < ranges_len) : (range_i += 1) {
- const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
- extra_index += 1;
- const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
- extra_index += 1;
+ const range_items = case_vals.items[case_val_idx..][0..2];
+ extra_index += 2;
+ case_val_idx += 2;
+
+ const item_first_ref = range_items[0];
+ const item_last_ref = range_items[1];
- const item_first_ref = try sema.resolveInst(first_ref);
var item = sema.resolveConstValue(block, .unneeded, item_first_ref, undefined) catch unreachable;
- const item_last_ref = try sema.resolveInst(last_ref);
const item_last = sema.resolveConstValue(block, .unneeded, item_last_ref, undefined) catch unreachable;
while (item.compareScalar(.lte, item_last, operand_ty, mod)) : ({
@@ -11217,7 +11627,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
cases_len += 1;
const item_ref = try sema.addConstant(operand_ty, item);
- case_block.inline_case_capture = item_ref;
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = child_block.wip_capture_scope;
@@ -11233,22 +11642,28 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
};
emit_bb = true;
- try sema.analyzeBodyRuntimeBreak(&case_block, body);
+ try spa.analyzeProngRuntime(
+ &case_block,
+ .normal,
+ body,
+ info.capture,
+ .{ .multi_capture = multi_i },
+ undefined, // case_vals may be undefined for ranges
+ item_ref,
+ info.has_tag_capture,
+ );
try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
cases_extra.appendAssumeCapacity(1); // items_len
cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
- cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendAssumeCapacity(@enumToInt(item_ref));
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
}
}
- for (items, 0..) |item_ref, item_i| {
+ for (items, 0..) |item, item_i| {
cases_len += 1;
- const item = try sema.resolveInst(item_ref);
- case_block.inline_case_capture = item;
-
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = child_block.wip_capture_scope;
@@ -11270,7 +11685,16 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
emit_bb = true;
if (analyze_body) {
- try sema.analyzeBodyRuntimeBreak(&case_block, body);
+ try spa.analyzeProngRuntime(
+ &case_block,
+ .normal,
+ body,
+ info.capture,
+ .{ .multi_capture = multi_i },
+ &.{item},
+ item,
+ info.has_tag_capture,
+ );
} else {
_ = try case_block.addNoOp(.unreach);
}
@@ -11278,11 +11702,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
cases_extra.appendAssumeCapacity(1); // items_len
cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
- cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendAssumeCapacity(@enumToInt(item));
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
}
- extra_index += body_len;
+ extra_index += info.body_len;
continue;
}
@@ -11295,8 +11719,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
cases_len += 1;
const analyze_body = if (union_originally)
- for (items) |item_ref| {
- const item = try sema.resolveInst(item_ref);
+ for (items) |item| {
const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable;
const field_ty = maybe_union_ty.unionFieldType(item_val, mod);
if (field_ty.zigTypeTag(mod) != .NoReturn) break true;
@@ -11304,12 +11727,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
else
true;
- const body = sema.code.extra[extra_index..][0..body_len];
- extra_index += body_len;
+ const body = sema.code.extra[extra_index..][0..info.body_len];
+ extra_index += info.body_len;
if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
// nothing to do here
} else if (analyze_body) {
- try sema.analyzeBodyRuntimeBreak(&case_block, body);
+ try spa.analyzeProngRuntime(
+ &case_block,
+ .normal,
+ body,
+ info.capture,
+ .{ .multi_capture = multi_i },
+ items,
+ .none,
+ false,
+ );
} else {
_ = try case_block.addNoOp(.unreach);
}
@@ -11320,15 +11752,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
cases_extra.appendAssumeCapacity(@intCast(u32, items.len));
cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
- for (items) |item_ref| {
- const item = try sema.resolveInst(item_ref);
+ for (items) |item| {
cases_extra.appendAssumeCapacity(@enumToInt(item));
}
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
} else {
- for (items) |item_ref| {
- const item = try sema.resolveInst(item_ref);
+ for (items) |item| {
const cmp_ok = try case_block.addBinOp(if (case_block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, item);
if (any_ok != .none) {
any_ok = try case_block.addBinOp(.bool_or, any_ok, cmp_ok);
@@ -11339,13 +11769,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
var range_i: usize = 0;
while (range_i < ranges_len) : (range_i += 1) {
- const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
- extra_index += 1;
- const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
- extra_index += 1;
+ const range_items = case_vals.items[case_val_idx..][0..2];
+ extra_index += 2;
+ case_val_idx += 2;
- const item_first = try sema.resolveInst(first_ref);
- const item_last = try sema.resolveInst(last_ref);
+ const item_first = range_items[0];
+ const item_last = range_items[1];
// operand >= first and operand <= last
const range_first_ok = try case_block.addBinOp(
@@ -11385,12 +11814,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = wip_captures.scope;
- const body = sema.code.extra[extra_index..][0..body_len];
- extra_index += body_len;
+ const body = sema.code.extra[extra_index..][0..info.body_len];
+ extra_index += info.body_len;
if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
// nothing to do here
} else {
- try sema.analyzeBodyRuntimeBreak(&case_block, body);
+ try spa.analyzeProngRuntime(
+ &case_block,
+ .normal,
+ body,
+ info.capture,
+ .{ .multi_capture = multi_i },
+ items,
+ .none,
+ false,
+ );
}
try wip_captures.finalize();
@@ -11435,7 +11873,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const item_val = try mod.enumValueFieldIndex(operand_ty, @intCast(u32, i));
const item_ref = try sema.addConstant(operand_ty, item_val);
- case_block.inline_case_capture = item_ref;
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = child_block.wip_capture_scope;
@@ -11449,7 +11886,16 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
emit_bb = true;
if (analyze_body) {
- try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+ try spa.analyzeProngRuntime(
+ &case_block,
+ .special,
+ special.body,
+ special.capture,
+ .special_capture,
+ &.{item_ref},
+ item_ref,
+ special.has_tag_capture,
+ );
} else {
_ = try case_block.addNoOp(.unreach);
}
@@ -11457,7 +11903,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
cases_extra.appendAssumeCapacity(1); // items_len
cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
- cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendAssumeCapacity(@enumToInt(item_ref));
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
}
},
@@ -11477,7 +11923,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.name = error_name,
} });
const item_ref = try sema.addConstant(operand_ty, item_val.toValue());
- case_block.inline_case_capture = item_ref;
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = child_block.wip_capture_scope;
@@ -11485,12 +11930,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
emit_bb = true;
- try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+ try spa.analyzeProngRuntime(
+ &case_block,
+ .special,
+ special.body,
+ special.capture,
+ .special_capture,
+ &.{item_ref},
+ item_ref,
+ special.has_tag_capture,
+ );
try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
cases_extra.appendAssumeCapacity(1); // items_len
cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
- cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendAssumeCapacity(@enumToInt(item_ref));
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
}
},
@@ -11500,7 +11954,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
cases_len += 1;
const item_ref = try sema.addConstant(operand_ty, cur.toValue());
- case_block.inline_case_capture = item_ref;
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = child_block.wip_capture_scope;
@@ -11508,19 +11961,27 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
emit_bb = true;
- try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+ try spa.analyzeProngRuntime(
+ &case_block,
+ .special,
+ special.body,
+ special.capture,
+ .special_capture,
+ &.{item_ref},
+ item_ref,
+ special.has_tag_capture,
+ );
try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
cases_extra.appendAssumeCapacity(1); // items_len
cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
- cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendAssumeCapacity(@enumToInt(item_ref));
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
}
},
.Bool => {
if (true_count == 0) {
cases_len += 1;
- case_block.inline_case_capture = Air.Inst.Ref.bool_true;
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = child_block.wip_capture_scope;
@@ -11528,17 +11989,25 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
emit_bb = true;
- try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+ try spa.analyzeProngRuntime(
+ &case_block,
+ .special,
+ special.body,
+ special.capture,
+ .special_capture,
+ &.{Air.Inst.Ref.bool_true},
+ Air.Inst.Ref.bool_true,
+ special.has_tag_capture,
+ );
try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
cases_extra.appendAssumeCapacity(1); // items_len
cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
- cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendAssumeCapacity(@enumToInt(Air.Inst.Ref.bool_true));
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
}
if (false_count == 0) {
cases_len += 1;
- case_block.inline_case_capture = Air.Inst.Ref.bool_false;
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = child_block.wip_capture_scope;
@@ -11546,12 +12015,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
emit_bb = true;
- try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+ try spa.analyzeProngRuntime(
+ &case_block,
+ .special,
+ special.body,
+ special.capture,
+ .special_capture,
+ &.{Air.Inst.Ref.bool_false},
+ Air.Inst.Ref.bool_false,
+ special.has_tag_capture,
+ );
try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
cases_extra.appendAssumeCapacity(1); // items_len
cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
- cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendAssumeCapacity(@enumToInt(Air.Inst.Ref.bool_false));
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
}
},
@@ -11565,7 +12043,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = wip_captures.scope;
- case_block.inline_case_capture = .none;
if (mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and
operand_ty.zigTypeTag(mod) == .Enum and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally))
@@ -11589,7 +12066,16 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
{
// nothing to do here
} else if (special.body.len != 0 and analyze_body and !special.is_inline) {
- try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+ try spa.analyzeProngRuntime(
+ &case_block,
+ .special,
+ special.body,
+ special.capture,
+ .special_capture,
+ undefined, // case_vals may be undefined for special prongs
+ .none,
+ false,
+ );
} else {
// We still need a terminator in this block, but we have proven
// that it is unreachable.
@@ -11704,29 +12190,51 @@ const RangeSetUnhandledIterator = struct {
}
};
+const ResolvedSwitchItem = struct {
+ ref: Air.Inst.Ref,
+ val: InternPool.Index,
+};
fn resolveSwitchItemVal(
sema: *Sema,
block: *Block,
item_ref: Zir.Inst.Ref,
+ /// Coerce `item_ref` to this type.
+ coerce_ty: Type,
switch_node_offset: i32,
switch_prong_src: Module.SwitchProngSrc,
range_expand: Module.SwitchProngSrc.RangeExpand,
-) CompileError!InternPool.Index {
+) CompileError!ResolvedSwitchItem {
const mod = sema.mod;
- const item = try sema.resolveInst(item_ref);
+ const uncoerced_item = try sema.resolveInst(item_ref);
+
// Constructing a LazySrcLoc is costly because we only have the switch AST node.
// Only if we know for sure we need to report a compile error do we resolve the
// full source locations.
- if (sema.resolveConstLazyValue(block, .unneeded, item, "")) |val| {
- return val.toIntern();
- } else |err| switch (err) {
+
+ const item = sema.coerce(block, coerce_ty, uncoerced_item, .unneeded) catch |err| switch (err) {
+ error.NeededSourceLocation => {
+ const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand);
+ _ = try sema.coerce(block, coerce_ty, uncoerced_item, src);
+ unreachable;
+ },
+ else => |e| return e,
+ };
+
+ const maybe_lazy = sema.resolveConstValue(block, .unneeded, item, "") catch |err| switch (err) {
error.NeededSourceLocation => {
const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand);
_ = try sema.resolveConstValue(block, src, item, "switch prong values must be comptime-known");
unreachable;
},
else => |e| return e,
- }
+ };
+
+ const val = try sema.resolveLazyValue(maybe_lazy);
+ const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: {
+ break :blk try sema.addConstant(coerce_ty, val);
+ } else item;
+
+ return .{ .ref = new_item, .val = val.toIntern() };
}
fn validateSwitchRange(
@@ -11735,31 +12243,35 @@ fn validateSwitchRange(
range_set: *RangeSet,
first_ref: Zir.Inst.Ref,
last_ref: Zir.Inst.Ref,
+ operand_ty: Type,
src_node_offset: i32,
switch_prong_src: Module.SwitchProngSrc,
-) CompileError!void {
+) CompileError![2]Air.Inst.Ref {
const mod = sema.mod;
- const first = try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first);
- const last = try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last);
- if (first.toValue().compareScalar(.gt, last.toValue(), mod.intern_pool.typeOf(first).toType(), mod)) {
+ const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, src_node_offset, switch_prong_src, .first);
+ const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, src_node_offset, switch_prong_src, .last);
+ if (try first.val.toValue().compareAll(.gt, last.val.toValue(), operand_ty, mod)) {
const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), src_node_offset, .first);
return sema.fail(block, src, "range start value is greater than the end value", .{});
}
- const maybe_prev_src = try range_set.add(first, last, switch_prong_src);
- return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+ const maybe_prev_src = try range_set.add(first.val, last.val, switch_prong_src);
+ try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+ return .{ first.ref, last.ref };
}
-fn validateSwitchItem(
+fn validateSwitchItemInt(
sema: *Sema,
block: *Block,
range_set: *RangeSet,
item_ref: Zir.Inst.Ref,
+ operand_ty: Type,
src_node_offset: i32,
switch_prong_src: Module.SwitchProngSrc,
-) CompileError!void {
- const item = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
- const maybe_prev_src = try range_set.add(item, item, switch_prong_src);
- return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+) CompileError!Air.Inst.Ref {
+ const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
+ const maybe_prev_src = try range_set.add(item.val, item.val, switch_prong_src);
+ try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+ return item.ref;
}
fn validateSwitchItemEnum(
@@ -11768,19 +12280,22 @@ fn validateSwitchItemEnum(
seen_fields: []?Module.SwitchProngSrc,
range_set: *RangeSet,
item_ref: Zir.Inst.Ref,
+ operand_ty: Type,
src_node_offset: i32,
switch_prong_src: Module.SwitchProngSrc,
-) CompileError!void {
+) CompileError!Air.Inst.Ref {
const ip = &sema.mod.intern_pool;
- const item = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
- const int = ip.indexToKey(item).enum_tag.int;
- const field_index = ip.indexToKey(ip.typeOf(item)).enum_type.tagValueIndex(ip, int) orelse {
+ const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
+ const int = ip.indexToKey(item.val).enum_tag.int;
+ const field_index = ip.indexToKey(ip.typeOf(item.val)).enum_type.tagValueIndex(ip, int) orelse {
const maybe_prev_src = try range_set.add(int, int, switch_prong_src);
- return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+ try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+ return item.ref;
};
const maybe_prev_src = seen_fields[field_index];
seen_fields[field_index] = switch_prong_src;
- return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+ try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+ return item.ref;
}
fn validateSwitchItemError(
@@ -11788,18 +12303,19 @@ fn validateSwitchItemError(
block: *Block,
seen_errors: *SwitchErrorSet,
item_ref: Zir.Inst.Ref,
+ operand_ty: Type,
src_node_offset: i32,
switch_prong_src: Module.SwitchProngSrc,
-) CompileError!void {
+) CompileError!Air.Inst.Ref {
const ip = &sema.mod.intern_pool;
- const item = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
- // TODO: Do i need to typecheck here?
- const error_name = ip.indexToKey(item).err.name;
+ const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
+ const error_name = ip.indexToKey(item.val).err.name;
const maybe_prev_src = if (try seen_errors.fetchPut(error_name, switch_prong_src)) |prev|
prev.value
else
null;
- return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+ try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+ return item.ref;
}
fn validateSwitchDupe(
@@ -11842,19 +12358,20 @@ fn validateSwitchItemBool(
item_ref: Zir.Inst.Ref,
src_node_offset: i32,
switch_prong_src: Module.SwitchProngSrc,
-) CompileError!void {
+) CompileError!Air.Inst.Ref {
const mod = sema.mod;
- const item = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
- if (item.toValue().toBool()) {
+ const item = try sema.resolveSwitchItemVal(block, item_ref, Type.bool, src_node_offset, switch_prong_src, .none);
+ if (item.val.toValue().toBool()) {
true_count.* += 1;
} else {
false_count.* += 1;
}
- if (true_count.* + false_count.* > 2) {
- const block_src_decl = mod.declPtr(block.src_decl);
+ if (true_count.* > 1 or false_count.* > 1) {
+ const block_src_decl = sema.mod.declPtr(block.src_decl);
const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none);
return sema.fail(block, src, "duplicate switch value", .{});
}
+ return item.ref;
}
const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, Module.SwitchProngSrc);
@@ -11864,12 +12381,14 @@ fn validateSwitchItemSparse(
block: *Block,
seen_values: *ValueSrcMap,
item_ref: Zir.Inst.Ref,
+ operand_ty: Type,
src_node_offset: i32,
switch_prong_src: Module.SwitchProngSrc,
-) CompileError!void {
- const item = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
- const kv = (try seen_values.fetchPut(sema.gpa, item, switch_prong_src)) orelse return;
- return sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset);
+) CompileError!Air.Inst.Ref {
+ const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none);
+ const kv = (try seen_values.fetchPut(sema.gpa, item.val, switch_prong_src)) orelse return item.ref;
+ try sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset);
+ unreachable;
}
fn validateSwitchNoRange(
diff --git a/src/Zir.zig b/src/Zir.zig
index c3a5f8e09b..81b02bb469 100644
--- a/src/Zir.zig
+++ b/src/Zir.zig
@@ -667,38 +667,9 @@ pub const Inst = struct {
/// A switch expression. Uses the `pl_node` union field.
/// AST node is the switch, payload is `SwitchBlock`.
switch_block,
- /// Produces the value that will be switched on. For example, for
- /// integers, it returns the integer with no modifications. For tagged unions, it
- /// returns the active enum tag.
- /// Uses the `un_node` union field.
- switch_cond,
- /// Same as `switch_cond`, except the input operand is a pointer to
- /// what will be switched on.
- /// Uses the `un_node` union field.
- switch_cond_ref,
- /// Produces the capture value for a switch prong.
- /// Uses the `switch_capture` field.
- /// If the `prong_index` field is max int, it means this is the capture
- /// for the else/`_` prong.
- switch_capture,
- /// Produces the capture value for a switch prong.
- /// Result is a pointer to the value.
- /// Uses the `switch_capture` field.
- /// If the `prong_index` field is max int, it means this is the capture
- /// for the else/`_` prong.
- 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 an inline switch prong tag capture.
- /// Uses the `un_tok` field.
- switch_capture_tag,
+ /// A switch expression. Uses the `pl_node` union field.
+ /// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer.
+ switch_block_ref,
/// Given a
/// *A returns *A
/// *E!A returns *A
@@ -1144,14 +1115,8 @@ pub const Inst = struct {
.typeof_log2_int_type,
.resolve_inferred_alloc,
.set_eval_branch_quota,
- .switch_capture,
- .switch_capture_ref,
- .switch_capture_multi,
- .switch_capture_multi_ref,
- .switch_capture_tag,
.switch_block,
- .switch_cond,
- .switch_cond_ref,
+ .switch_block_ref,
.array_base_ptr,
.field_base_ptr,
.validate_array_init_ty,
@@ -1438,14 +1403,8 @@ pub const Inst = struct {
.slice_length,
.import,
.typeof_log2_int_type,
- .switch_capture,
- .switch_capture_ref,
- .switch_capture_multi,
- .switch_capture_multi_ref,
- .switch_capture_tag,
.switch_block,
- .switch_cond,
- .switch_cond_ref,
+ .switch_block_ref,
.array_base_ptr,
.field_base_ptr,
.struct_init_empty,
@@ -1696,13 +1655,7 @@ pub const Inst = struct {
.err_union_code_ptr = .un_node,
.enum_literal = .str_tok,
.switch_block = .pl_node,
- .switch_cond = .un_node,
- .switch_cond_ref = .un_node,
- .switch_capture = .switch_capture,
- .switch_capture_ref = .switch_capture,
- .switch_capture_multi = .switch_capture,
- .switch_capture_multi_ref = .switch_capture,
- .switch_capture_tag = .un_tok,
+ .switch_block_ref = .pl_node,
.array_base_ptr = .un_node,
.field_base_ptr = .un_node,
.validate_array_init_ty = .pl_node,
@@ -2028,9 +1981,10 @@ pub const Inst = struct {
/// Implements the `@inComptime` builtin.
/// `operand` is `src_node: i32`.
in_comptime,
- /// Used as a placeholder for the capture of an `errdefer`.
- /// This is replaced by Sema with the captured value.
- errdefer_err_code,
+ /// Used as a placeholder instruction which is just a dummy index for Sema to replace
+ /// with a specific value. For instance, this is used for the capture of an `errdefer`.
+ /// This should never appear in a body.
+ value_placeholder,
pub const InstData = struct {
opcode: Extended,
@@ -2269,10 +2223,6 @@ pub const Inst = struct {
operand: Ref,
payload_index: u32,
},
- switch_capture: struct {
- switch_inst: Index,
- prong_index: u32,
- },
dbg_stmt: LineColumn,
/// Used for unary operators which reference an inst,
/// with an AST node source location.
@@ -2342,7 +2292,6 @@ pub const Inst = struct {
bool_br,
@"unreachable",
@"break",
- switch_capture,
dbg_stmt,
inst_node,
str_op,
@@ -2681,37 +2630,53 @@ pub const Inst = struct {
};
/// 0. multi_cases_len: u32 // If has_multi_cases is set.
- /// 1. else_body { // If has_else or has_under is set.
- /// body_len: u32,
- /// body member Index for every body_len
+ /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture.
+ /// 2. else_body { // If has_else or has_under is set.
+ /// info: ProngInfo,
+ /// body member Index for every info.body_len
/// }
- /// 2. scalar_cases: { // for every scalar_cases_len
+ /// 3. scalar_cases: { // for every scalar_cases_len
/// item: Ref,
- /// body_len: u32,
- /// body member Index for every body_len
+ /// info: ProngInfo,
+ /// body member Index for every info.body_len
/// }
- /// 3. multi_cases: { // for every multi_cases_len
+ /// 4. multi_cases: { // for every multi_cases_len
/// items_len: u32,
/// ranges_len: u32,
- /// body_len: u32,
+ /// info: ProngInfo,
/// item: Ref // for every items_len
/// ranges: { // for every ranges_len
/// item_first: Ref,
/// item_last: Ref,
/// }
- /// body member Index for every body_len
+ /// body member Index for every info.body_len
/// }
+ ///
+ /// When analyzing a case body, the switch instruction itself refers to the
+ /// captured payload. Whether this is captured by reference or by value
+ /// depends on whether the `byref` bit is set for the corresponding body.
pub const SwitchBlock = struct {
- /// This is always a `switch_cond` or `switch_cond_ref` instruction.
- /// If it is a `switch_cond_ref` instruction, bits.is_ref is always true.
- /// If it is a `switch_cond` instruction, bits.is_ref is always false.
- /// Both `switch_cond` and `switch_cond_ref` return a value, not a pointer,
- /// that is useful for the case items, but cannot be used for capture values.
- /// For the capture values, Sema is expected to find the operand of this operand
- /// and use that.
+ /// The operand passed to the `switch` expression. If this is a
+ /// `switch_block`, this is the operand value; if `switch_block_ref` it
+ /// is a pointer to the operand. `switch_block_ref` is always used if
+ /// any prong has a byref capture.
operand: Ref,
bits: Bits,
+ /// These are stored in trailing data in `extra` for each prong.
+ pub const ProngInfo = packed struct(u32) {
+ body_len: u28,
+ capture: Capture,
+ is_inline: bool,
+ has_tag_capture: bool,
+
+ pub const Capture = enum(u2) {
+ none,
+ by_val,
+ by_ref,
+ };
+ };
+
pub const Bits = packed struct {
/// If true, one or more prongs have multiple items.
has_multi_cases: bool,
@@ -2719,9 +2684,11 @@ pub const Inst = struct {
has_else: bool,
/// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
has_under: bool,
+ /// If true, at least one prong has an inline tag capture.
+ any_has_tag_capture: bool,
scalar_cases_len: ScalarCasesLen,
- pub const ScalarCasesLen = u29;
+ pub const ScalarCasesLen = u28;
pub fn specialProng(bits: Bits) SpecialProng {
const has_else: u2 = @boolToInt(bits.has_else);
@@ -2735,103 +2702,10 @@ pub const Inst = struct {
}
};
- pub const ScalarProng = struct {
- item: Ref,
- body: []const Index,
- };
-
- /// TODO performance optimization: instead of having this helper method
- /// change the definition of switch_capture instruction to store extra_index
- /// instead of prong_index. This way, Sema won't be doing O(N^2) iterations
- /// over the switch prongs.
- pub fn getScalarProng(
- self: SwitchBlock,
- zir: Zir,
- extra_end: usize,
- prong_index: usize,
- ) ScalarProng {
- var extra_index: usize = extra_end;
-
- if (self.bits.has_multi_cases) {
- extra_index += 1;
- }
-
- if (self.bits.specialProng() != .none) {
- const body_len = @truncate(u31, zir.extra[extra_index]);
- extra_index += 1;
- const body = zir.extra[extra_index..][0..body_len];
- extra_index += body.len;
- }
-
- var scalar_i: usize = 0;
- while (true) : (scalar_i += 1) {
- const item = @intToEnum(Ref, zir.extra[extra_index]);
- extra_index += 1;
- const body_len = @truncate(u31, zir.extra[extra_index]);
- extra_index += 1;
- const body = zir.extra[extra_index..][0..body_len];
- extra_index += body.len;
-
- if (scalar_i < prong_index) continue;
-
- return .{
- .item = item,
- .body = body,
- };
- }
- }
-
pub const MultiProng = struct {
items: []const Ref,
body: []const Index,
};
-
- pub fn getMultiProng(
- self: SwitchBlock,
- zir: Zir,
- extra_end: usize,
- prong_index: usize,
- ) MultiProng {
- // +1 for self.bits.has_multi_cases == true
- var extra_index: usize = extra_end + 1;
-
- if (self.bits.specialProng() != .none) {
- const body_len = @truncate(u31, zir.extra[extra_index]);
- extra_index += 1;
- const body = zir.extra[extra_index..][0..body_len];
- extra_index += body.len;
- }
-
- var scalar_i: usize = 0;
- while (scalar_i < self.bits.scalar_cases_len) : (scalar_i += 1) {
- extra_index += 1;
- const body_len = @truncate(u31, zir.extra[extra_index]);
- extra_index += 1;
- extra_index += body_len;
- }
- var multi_i: u32 = 0;
- while (true) : (multi_i += 1) {
- const items_len = zir.extra[extra_index];
- extra_index += 1;
- const ranges_len = zir.extra[extra_index];
- extra_index += 1;
- const body_len = @truncate(u31, zir.extra[extra_index]);
- extra_index += 1;
- const items = zir.refSlice(extra_index, items_len);
- extra_index += items_len;
- // Each range has a start and an end.
- extra_index += 2 * ranges_len;
-
- const body = zir.extra[extra_index..][0..body_len];
- extra_index += body_len;
-
- if (multi_i < prong_index) continue;
- return .{
- .items = items,
- .body = body,
- };
- }
- }
};
pub const Field = struct {
diff --git a/src/print_zir.zig b/src/print_zir.zig
index 6c371b8b8d..35050f1728 100644
--- a/src/print_zir.zig
+++ b/src/print_zir.zig
@@ -222,8 +222,6 @@ const Writer = struct {
.bit_reverse,
.@"resume",
.@"await",
- .switch_cond,
- .switch_cond_ref,
.array_base_ptr,
.field_base_ptr,
.validate_struct_init_ty,
@@ -235,7 +233,6 @@ const Writer = struct {
.ref,
.ret_implicit,
.closure_capture,
- .switch_capture_tag,
=> try self.writeUnTok(stream, inst),
.bool_br_and,
@@ -389,7 +386,9 @@ const Writer = struct {
.error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
.error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
- .switch_block => try self.writeSwitchBlock(stream, inst),
+ .switch_block,
+ .switch_block_ref,
+ => try self.writeSwitchBlock(stream, inst),
.field_ptr,
.field_ptr_init,
@@ -436,12 +435,6 @@ const Writer = struct {
.@"unreachable" => try self.writeUnreachable(stream, inst),
- .switch_capture,
- .switch_capture_ref,
- .switch_capture_multi,
- .switch_capture_multi_ref,
- => try self.writeSwitchCapture(stream, inst),
-
.dbg_stmt => try self.writeDbgStmt(stream, inst),
.dbg_block_begin,
@@ -469,7 +462,7 @@ const Writer = struct {
.breakpoint,
.c_va_start,
.in_comptime,
- .errdefer_err_code,
+ .value_placeholder,
=> try self.writeExtNode(stream, extended),
.builtin_src => {
@@ -1903,8 +1896,19 @@ const Writer = struct {
break :blk multi_cases_len;
} else 0;
+ const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
+ const tag_capture_inst = self.code.extra[extra_index];
+ extra_index += 1;
+ break :blk tag_capture_inst;
+ } else undefined;
+
try self.writeInstRef(stream, extra.data.operand);
+ if (extra.data.bits.any_has_tag_capture) {
+ try stream.writeAll(", tag_capture=");
+ try self.writeInstIndex(stream, tag_capture_inst);
+ }
+
self.indent += 2;
else_prong: {
@@ -1915,15 +1919,20 @@ const Writer = struct {
else => break :else_prong,
};
- const body_len = @truncate(u31, self.code.extra[extra_index]);
- const inline_text = if (self.code.extra[extra_index] >> 31 != 0) "inline " else "";
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, self.code.extra[extra_index]);
+ const capture_text = switch (info.capture) {
+ .none => "",
+ .by_val => "by_val ",
+ .by_ref => "by_ref ",
+ };
+ const inline_text = if (info.is_inline) "inline " else "";
extra_index += 1;
- const body = self.code.extra[extra_index..][0..body_len];
+ const body = self.code.extra[extra_index..][0..info.body_len];
extra_index += body.len;
try stream.writeAll(",\n");
try stream.writeByteNTimes(' ', self.indent);
- try stream.print("{s}{s} => ", .{ inline_text, prong_name });
+ try stream.print("{s}{s}{s} => ", .{ capture_text, inline_text, prong_name });
try self.writeBracedBody(stream, body);
}
@@ -1933,15 +1942,19 @@ const Writer = struct {
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
extra_index += 1;
- const body_len = @truncate(u31, self.code.extra[extra_index]);
- const is_inline = self.code.extra[extra_index] >> 31 != 0;
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, self.code.extra[extra_index]);
extra_index += 1;
- const body = self.code.extra[extra_index..][0..body_len];
- extra_index += body_len;
+ const body = self.code.extra[extra_index..][0..info.body_len];
+ extra_index += info.body_len;
try stream.writeAll(",\n");
try stream.writeByteNTimes(' ', self.indent);
- if (is_inline) try stream.writeAll("inline ");
+ switch (info.capture) {
+ .none => {},
+ .by_val => try stream.writeAll("by_val "),
+ .by_ref => try stream.writeAll("by_ref "),
+ }
+ if (info.is_inline) try stream.writeAll("inline ");
try self.writeInstRef(stream, item_ref);
try stream.writeAll(" => ");
try self.writeBracedBody(stream, body);
@@ -1954,15 +1967,19 @@ const Writer = struct {
extra_index += 1;
const ranges_len = self.code.extra[extra_index];
extra_index += 1;
- const body_len = @truncate(u31, self.code.extra[extra_index]);
- const is_inline = self.code.extra[extra_index] >> 31 != 0;
+ const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, self.code.extra[extra_index]);
extra_index += 1;
const items = self.code.refSlice(extra_index, items_len);
extra_index += items_len;
try stream.writeAll(",\n");
try stream.writeByteNTimes(' ', self.indent);
- if (is_inline) try stream.writeAll("inline ");
+ switch (info.capture) {
+ .none => {},
+ .by_val => try stream.writeAll("by_val "),
+ .by_ref => try stream.writeAll("by_ref "),
+ }
+ if (info.is_inline) try stream.writeAll("inline ");
for (items, 0..) |item_ref, item_i| {
if (item_i != 0) try stream.writeAll(", ");
@@ -1984,8 +2001,8 @@ const Writer = struct {
try self.writeInstRef(stream, item_last);
}
- const body = self.code.extra[extra_index..][0..body_len];
- extra_index += body_len;
+ const body = self.code.extra[extra_index..][0..info.body_len];
+ extra_index += info.body_len;
try stream.writeAll(" => ");
try self.writeBracedBody(stream, body);
}
@@ -2437,12 +2454,6 @@ const Writer = struct {
try self.writeSrc(stream, src);
}
- fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Zir.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 writeDbgStmt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].dbg_stmt;
try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 });