Eliminate switch_capture_tag ZIR instruction

This is a follow-up to a previous commit which eliminated switch_capture
and switch_capture_ref. All captures are now handled directly by
`switch_block`, which has also eliminated some unnecessary Block data in
Sema.
This commit is contained in:
mlugg 2023-05-27 07:29:55 +01:00
parent ec27524da9
commit 39510cc7d1
No known key found for this signature in database
GPG Key ID: 58978E823BDE3EF9
4 changed files with 194 additions and 91 deletions

View File

@ -2612,7 +2612,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.switch_block,
.switch_cond,
.switch_cond_ref,
.switch_capture_tag,
.struct_init_empty,
.struct_init,
.struct_init_ref,
@ -2956,7 +2955,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,
} },
@ -6711,6 +6710,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;
@ -6721,8 +6721,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.
@ -6861,6 +6865,20 @@ fn switchExpr(
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;
@ -6874,7 +6892,7 @@ 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 tag_inst: Zir.Inst.Index = 0;
var has_tag_capture = false;
var capture_val_scope: Scope.LocalVal = undefined;
var tag_scope: Scope.LocalVal = undefined;
@ -6925,14 +6943,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,
@ -6998,7 +7011,6 @@ fn switchExpr(
case_scope.instructions_top = parent_gz.instructions.items.len;
defer case_scope.unstack();
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);
@ -7018,7 +7030,8 @@ fn switchExpr(
const case_slice = case_scope.instructionsSlice();
// 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.
// 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;
@ -7026,18 +7039,31 @@ fn switchExpr(
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);
payloads.items[body_len_index] = @bitCast(u32, Zir.Inst.SwitchBlock.ProngInfo{
.body_len = @intCast(u29, body_len),
.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);
}
}
@ -7046,6 +7072,7 @@ 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{
@ -7054,6 +7081,7 @@ fn switchExpr(
.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),
},
});
@ -7062,6 +7090,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);

View File

@ -277,9 +277,6 @@ pub const Block = struct {
c_import_buf: ?*std.ArrayList(u8) = 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,
@ -1013,7 +1010,6 @@ fn analyzeBodyInner(
.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_tag => try sema.zirSwitchCaptureTag(block, inst),
.type_info => try sema.zirTypeInfo(block, inst),
.size_of => try sema.zirSizeOf(block, inst),
.bit_size_of => try sema.zirBitSizeOf(block, inst),
@ -1217,7 +1213,7 @@ fn analyzeBodyInner(
i += 1;
continue;
},
.errdefer_err_code => unreachable, // never appears in a body
.value_placeholder => unreachable, // never appears in a body
};
},
@ -10092,6 +10088,9 @@ const SwitchProngAnalysis = struct {
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.
@ -10106,10 +10105,23 @@ const SwitchProngAnalysis = struct {
/// 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);
@ -10122,6 +10134,7 @@ const SwitchProngAnalysis = struct {
prong_type == .special,
raw_capture_src,
case_vals,
inline_case_capture,
);
if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) {
@ -10150,8 +10163,21 @@ const SwitchProngAnalysis = struct {
/// 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);
@ -10164,6 +10190,7 @@ const SwitchProngAnalysis = struct {
prong_type == .special,
raw_capture_src,
case_vals,
inline_case_capture,
);
if (sema.typeOf(capture_ref).isNoReturn(sema.mod)) {
@ -10179,6 +10206,33 @@ const SwitchProngAnalysis = struct {
}
}
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 capture_src = raw_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,
@ -10186,6 +10240,7 @@ const SwitchProngAnalysis = struct {
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;
@ -10197,8 +10252,8 @@ const SwitchProngAnalysis = struct {
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 (block.inline_case_capture != .none) {
const item_val = sema.resolveConstValue(block, .unneeded, block.inline_case_capture, "") catch unreachable;
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).?;
@ -10233,7 +10288,7 @@ const SwitchProngAnalysis = struct {
} else if (capture_byref) {
return sema.addConstantMaybeRef(block, operand_ty, item_val, true);
} else {
return block.inline_case_capture;
return inline_case_capture;
}
}
@ -10356,34 +10411,6 @@ const SwitchProngAnalysis = struct {
}
};
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();
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;
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);
}
return block.inline_case_capture;
}
fn zirSwitchCond(
sema: *Sema,
block: *Block,
@ -10485,6 +10512,16 @@ 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);
@ -10493,11 +10530,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
end: usize,
capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
is_inline: bool,
has_tag_capture: bool,
};
const special_prong = extra.data.bits.specialProng();
const special: Special = switch (special_prong) {
.none => .{ .body = &.{}, .end = header_extra_index, .capture = .none, .is_inline = false },
.none => .{
.body = &.{},
.end = header_extra_index,
.capture = .none,
.is_inline = false,
.has_tag_capture = false,
},
.under, .@"else" => blk: {
const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, sema.code.extra[header_extra_index]);
const extra_body_start = header_extra_index + 1;
@ -10506,6 +10550,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.end = extra_body_start + info.body_len,
.capture = info.capture,
.is_inline = info.is_inline,
.has_tag_capture = info.has_tag_capture,
};
},
};
@ -11068,6 +11113,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.operand_ptr = raw_operand.ptr,
.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);
@ -11122,7 +11168,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
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 (info.is_inline) child_block.inline_case_capture = operand;
if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
return spa.resolveProngComptime(
&child_block,
@ -11131,6 +11176,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
info.capture,
.{ .scalar = @intCast(u32, scalar_i) },
&.{item},
if (info.is_inline) operand else .none,
info.has_tag_capture,
merges,
);
}
@ -11155,7 +11202,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
// 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 (info.is_inline) child_block.inline_case_capture = operand;
if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
return spa.resolveProngComptime(
&child_block,
@ -11164,6 +11210,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
info.capture,
.{ .multi_capture = @intCast(u32, multi_i) },
items,
if (info.is_inline) operand else .none,
info.has_tag_capture,
merges,
);
}
@ -11181,7 +11229,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
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 (info.is_inline) child_block.inline_case_capture = operand;
if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
return spa.resolveProngComptime(
&child_block,
@ -11190,6 +11237,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
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,
);
}
@ -11199,7 +11248,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
}
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;
}
@ -11211,6 +11259,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
special.capture,
.special,
undefined, // case_vals may be undefined for special prongs
if (special.is_inline) operand else .none,
special.has_tag_capture,
merges,
);
}
@ -11240,6 +11290,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
special.capture,
.special,
undefined, // case_vals may be undefined for special prongs
.none,
false,
merges,
);
}
@ -11278,10 +11330,8 @@ 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;
const item = case_vals.items[scalar_i];
if (info.is_inline) case_block.inline_case_capture = item;
// `item` is already guaranteed to be constant known.
const analyze_body = if (union_originally) blk: {
@ -11300,6 +11350,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
info.capture,
.{ .scalar = @intCast(u32, scalar_i) },
&.{item},
if (info.is_inline) item else .none,
info.has_tag_capture,
);
} else {
_ = try case_block.addNoOp(.unreach);
@ -11337,7 +11389,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
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 (info.is_inline) {
@ -11367,7 +11418,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;
@ -11390,12 +11440,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
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);
}
}
@ -11403,8 +11455,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
for (items, 0..) |item, item_i| {
cases_len += 1;
case_block.inline_case_capture = item;
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = child_block.wip_capture_scope;
@ -11433,6 +11483,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
info.capture,
.{ .multi_capture = multi_i },
&.{item},
item,
info.has_tag_capture,
);
} else {
_ = try case_block.addNoOp(.unreach);
@ -11441,7 +11493,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));
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
}
@ -11478,6 +11530,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
info.capture,
.{ .multi_capture = multi_i },
items,
.none,
false,
);
} else {
_ = try case_block.addNoOp(.unreach);
@ -11563,6 +11617,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
info.capture,
.{ .multi_capture = multi_i },
items,
.none,
false,
);
}
@ -11608,7 +11664,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;
@ -11629,6 +11684,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
special.capture,
.special,
&.{item_ref},
item_ref,
special.has_tag_capture,
);
} else {
_ = try case_block.addNoOp(.unreach);
@ -11637,7 +11694,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);
}
},
@ -11657,7 +11714,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;
@ -11672,12 +11728,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
special.capture,
.special,
&.{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);
}
},
@ -11687,7 +11745,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;
@ -11702,19 +11759,20 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
special.capture,
.special,
&.{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;
@ -11729,17 +11787,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
special.capture,
.special,
&.{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;
@ -11754,12 +11813,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
special.capture,
.special,
&.{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);
}
},
@ -11773,7 +11834,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))
@ -11804,6 +11864,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
special.capture,
.special,
undefined, // case_vals may be undefined for special prongs
.none,
false,
);
} else {
// We still need a terminator in this block, but we have proven

View File

@ -676,9 +676,6 @@ pub const Inst = struct {
/// what will be switched on.
/// Uses the `un_node` union field.
switch_cond_ref,
/// Produces the capture value for an inline switch prong tag capture.
/// Uses the `un_tok` field.
switch_capture_tag,
/// Given a
/// *A returns *A
/// *E!A returns *A
@ -1124,7 +1121,6 @@ pub const Inst = struct {
.typeof_log2_int_type,
.resolve_inferred_alloc,
.set_eval_branch_quota,
.switch_capture_tag,
.switch_block,
.switch_cond,
.switch_cond_ref,
@ -1414,7 +1410,6 @@ pub const Inst = struct {
.slice_length,
.import,
.typeof_log2_int_type,
.switch_capture_tag,
.switch_block,
.switch_cond,
.switch_cond_ref,
@ -1670,7 +1665,6 @@ pub const Inst = struct {
.switch_block = .pl_node,
.switch_cond = .un_node,
.switch_cond_ref = .un_node,
.switch_capture_tag = .un_tok,
.array_base_ptr = .un_node,
.field_base_ptr = .un_node,
.validate_array_init_ty = .pl_node,
@ -1996,9 +1990,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,
@ -2644,16 +2639,17 @@ 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.
/// 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,
/// 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,
/// info: ProngInfo,
@ -2681,9 +2677,10 @@ pub const Inst = struct {
/// These are stored in trailing data in `extra` for each prong.
pub const ProngInfo = packed struct(u32) {
body_len: u29,
body_len: u28,
capture: Capture,
is_inline: bool,
has_tag_capture: bool,
pub const Capture = enum(u2) {
none,
@ -2699,9 +2696,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);

View File

@ -235,7 +235,6 @@ const Writer = struct {
.ref,
.ret_implicit,
.closure_capture,
.switch_capture_tag,
=> try self.writeUnTok(stream, inst),
.bool_br_and,
@ -463,7 +462,7 @@ const Writer = struct {
.breakpoint,
.c_va_start,
.in_comptime,
.errdefer_err_code,
.value_placeholder,
=> try self.writeExtNode(stream, extended),
.builtin_src => {
@ -1897,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: {