From a0631e7f7d38bcd689537160053930cad4df94ae Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 27 May 2023 07:29:55 +0100 Subject: [PATCH] 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. --- src/AstGen.zig | 62 ++++++++++++---- src/Sema.zig | 184 +++++++++++++++++++++++++++++++--------------- src/Zir.zig | 27 ++++--- src/print_zir.zig | 14 +++- 4 files changed, 195 insertions(+), 92 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index c17a331b80bf..87891046b64d 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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, } }, @@ -6695,6 +6694,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; @@ -6705,8 +6705,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. @@ -6845,6 +6849,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; @@ -6858,7 +6876,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; @@ -6909,14 +6927,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, @@ -6982,7 +6995,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); @@ -7002,7 +7014,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; @@ -7010,18 +7023,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); } } @@ -7030,6 +7056,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{ @@ -7038,6 +7065,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), }, }); @@ -7046,6 +7074,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); diff --git a/src/Sema.zig b/src/Sema.zig index aa8788ac5aa0..bc77e7ee3752 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -265,9 +265,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, @@ -985,7 +982,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), @@ -1189,7 +1185,7 @@ fn analyzeBodyInner( i += 1; continue; }, - .errdefer_err_code => unreachable, // never appears in a body + .value_placeholder => unreachable, // never appears in a body }; }, @@ -10061,6 +10057,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. @@ -10075,10 +10074,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); @@ -10091,6 +10103,7 @@ const SwitchProngAnalysis = struct { prong_type == .special, raw_capture_src, case_vals, + inline_case_capture, ); if (sema.typeOf(capture_ref).isNoReturn()) { @@ -10119,8 +10132,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); @@ -10133,6 +10159,7 @@ const SwitchProngAnalysis = struct { prong_type == .special, raw_capture_src, case_vals, + inline_case_capture, ); if (sema.typeOf(capture_ref).isNoReturn()) { @@ -10148,6 +10175,32 @@ 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 operand_ty = sema.typeOf(spa.operand); + if (operand_ty.zigTypeTag() != .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(sema.gpa, sema.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(sema.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, @@ -10155,6 +10208,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; @@ -10165,8 +10219,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() == .Union) { const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, sema.mod).?); const union_obj = operand_ty.cast(Type.Payload.Union).?.data; @@ -10205,7 +10259,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; } } @@ -10330,33 +10384,6 @@ const SwitchProngAnalysis = struct { } }; -fn zirSwitchCaptureTag(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - 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() else operand_ptr_ty; - - if (operand_ty.zigTypeTag() != .Union) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "cannot capture tag of non-union type '{}'", .{ - operand_ty.fmt(sema.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, @@ -10455,6 +10482,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); @@ -10463,11 +10500,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; @@ -10476,6 +10520,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, }; }, }; @@ -11047,6 +11092,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); @@ -11100,7 +11146,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, @@ -11109,6 +11154,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, ); } @@ -11133,7 +11180,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, @@ -11142,6 +11188,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, ); } @@ -11159,7 +11207,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if ((try sema.compareAll(operand_val, .gte, first_val, operand_ty)) and (try sema.compareAll(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, @@ -11168,6 +11215,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, ); } @@ -11177,7 +11226,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; } @@ -11189,6 +11237,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, ); } @@ -11218,6 +11268,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, ); } @@ -11256,10 +11308,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: { @@ -11278,6 +11328,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); @@ -11315,7 +11367,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) { @@ -11342,7 +11393,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; @@ -11365,12 +11415,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); } } @@ -11378,8 +11430,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; @@ -11408,6 +11458,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); @@ -11416,7 +11468,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); } @@ -11453,6 +11505,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); @@ -11538,6 +11592,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError info.capture, .{ .multi_capture = multi_i }, items, + .none, + false, ); } @@ -11583,7 +11639,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const item_val = try Value.Tag.enum_field_index.create(sema.arena, @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; @@ -11604,6 +11659,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); @@ -11612,7 +11669,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); } }, @@ -11628,7 +11685,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const item_val = try Value.Tag.@"error".create(sema.arena, .{ .name = error_name }); 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; @@ -11643,12 +11699,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); } }, @@ -11658,7 +11716,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError cases_len += 1; const item_ref = try sema.addConstant(operand_ty, cur); - case_block.inline_case_capture = item_ref; case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = child_block.wip_capture_scope; @@ -11673,19 +11730,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; @@ -11699,18 +11757,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError special.body, special.capture, .special, - &.{Zir.Inst.Ref.bool_true}, + &.{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; @@ -11724,13 +11783,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError special.body, special.capture, .special, - &.{Zir.Inst.Ref.bool_false}, + &.{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); } }, @@ -11744,7 +11805,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 (sema.mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and operand_ty.zigTypeTag() == .Enum and (!operand_ty.isNonexhaustiveEnum() or union_originally)) @@ -11775,6 +11835,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 diff --git a/src/Zir.zig b/src/Zir.zig index 5853acb815e9..556e0f8f2535 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -675,9 +675,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 @@ -1123,7 +1120,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, @@ -1413,7 +1409,6 @@ pub const Inst = struct { .slice_length, .import, .typeof_log2_int_type, - .switch_capture_tag, .switch_block, .switch_cond, .switch_cond_ref, @@ -1669,7 +1664,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, @@ -1995,9 +1989,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, @@ -2988,16 +2983,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, @@ -3025,9 +3021,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, @@ -3043,9 +3040,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); diff --git a/src/print_zir.zig b/src/print_zir.zig index a0c9728ea3cb..221dbed662c4 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -234,7 +234,6 @@ const Writer = struct { .ref, .ret_implicit, .closure_capture, - .switch_capture_tag, => try self.writeUnTok(stream, inst), .bool_br_and, @@ -462,7 +461,7 @@ const Writer = struct { .breakpoint, .c_va_start, .in_comptime, - .errdefer_err_code, + .value_placeholder, => try self.writeExtNode(stream, extended), .builtin_src => { @@ -1896,8 +1895,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: {