mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 02:44:43 +01:00
frontend: rework switch ZIR
Moved to a more linear layout which lends itself well to exposing an iterator.
Consumers of this iterator now just have to keep track of an index into a
homogenous sequence of bodies.
The new ZIR layout also enables giving switch prong items result locations
by storing the bodies of all items inside of the switch encoding itself.
There are some deliberate exceptions to this: enum literals and error values
are directly encoded as strings and number literals are resolved to comptime
values outside of the switch block. These special encodings exist to save
space and can easily be resolved during semantic analysis.
This commit also re-implements `AstGen` and `print_zir` for switch based on
the new layout and adds some additional information to the ZIR text repr.
Notably `switchExprErrUnion` has been merged into `switchExpr` to reduce
code duplication.
The rules around allowing an unreachable `else` prong in error switches are
also refined by this commit, and enforced properly based on the actual AST.
The special cases are listed exhaustively below:
`else => unreachable,`
`else => return,`
`else => |e| return e,` (where `e` is any identifier)
Additionally `{...} => comptime unreachable,` prongs are marked to support
future features (refer to next couple of commits).
Also fixes 'value with comptime-only type depends on runtime control flow'
error for labeled error switch statements by surrounding the entire expr
with a common block to break to (see previous commits for details).
This commit is contained in:
parent
00d4f3c001
commit
5b00e24b6e
3 changed files with 1435 additions and 1394 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -95,7 +95,6 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) {
|
|||
Inst.Call.Flags,
|
||||
Inst.BuiltinCall.Flags,
|
||||
Inst.SwitchBlock.Bits,
|
||||
Inst.SwitchBlockErrUnion.Bits,
|
||||
Inst.FuncFancy.Bits,
|
||||
Inst.Declaration.Flags,
|
||||
Inst.Param.Type,
|
||||
|
|
@ -350,7 +349,8 @@ pub const Inst = struct {
|
|||
/// Uses the `break` union field.
|
||||
break_inline,
|
||||
/// Branch from within a switch case to the case specified by the operand.
|
||||
/// Uses the `break` union field. `block_inst` refers to a `switch_block` or `switch_block_ref`.
|
||||
/// Uses the `break` union field. `block_inst` refers to a `switch_block`/
|
||||
/// `switch_block_ref`/`switch_block_err_union`.
|
||||
switch_continue,
|
||||
/// Checks that comptime control flow does not happen inside a runtime block.
|
||||
/// Uses the `un_node` union field.
|
||||
|
|
@ -722,8 +722,10 @@ pub const Inst = struct {
|
|||
/// A switch expression. Uses the `pl_node` union field.
|
||||
/// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer.
|
||||
switch_block_ref,
|
||||
/// A switch on an error union `a catch |err| switch (err) {...}`.
|
||||
/// Uses the `pl_node` union field. AST node is the `catch`, payload is `SwitchBlockErrUnion`.
|
||||
/// A switch on an error union:
|
||||
/// - `eu catch |err| switch (err) {...}`, AST node is the `catch`.
|
||||
/// - `if (eu) |payload| {...} else |err| {...}`, AST node is the `if`.
|
||||
/// Uses the `pl_node` union field. Payload is `SwitchBlock`.
|
||||
switch_block_err_union,
|
||||
/// Check that operand type supports the dereference operand (.*).
|
||||
/// Uses the `un_node` field.
|
||||
|
|
@ -3293,143 +3295,168 @@ pub const Inst = struct {
|
|||
};
|
||||
|
||||
/// Trailing:
|
||||
/// 0. multi_cases_len: u32 // if `has_multi_cases`
|
||||
/// 1. err_capture_inst: u32 // if `any_uses_err_capture`
|
||||
/// 2. non_err_body {
|
||||
/// info: ProngInfo,
|
||||
/// inst: Index // for every `info.body_len`
|
||||
/// }
|
||||
/// 3. else_body { // if `has_else`
|
||||
/// info: ProngInfo,
|
||||
/// inst: Index // for every `info.body_len`
|
||||
/// }
|
||||
/// 4. scalar_cases: { // for every `scalar_cases_len`
|
||||
/// item: Ref,
|
||||
/// info: ProngInfo,
|
||||
/// inst: Index // for every `info.body_len`
|
||||
/// }
|
||||
/// 5. multi_cases: { // for every `multi_cases_len`
|
||||
/// items_len: u32,
|
||||
/// ranges_len: u32,
|
||||
/// info: ProngInfo,
|
||||
/// item: Ref // for every `items_len`
|
||||
/// ranges: { // for every `ranges_len`
|
||||
/// item_first: Ref,
|
||||
/// item_last: Ref,
|
||||
/// 0. multi_cases_len: u32, // If has_multi_cases is set.
|
||||
/// 1. payload_capture_placeholder: Inst.Index, // If payload_capture_inst_is_placeholder is set.
|
||||
/// // Index of instruction prongs use to refer to their payload capture.
|
||||
/// 2. tag_capture_placeholder: Inst.Index, // If tag_capture_inst_is_placeholder is set.
|
||||
/// // Index of instruction prongs use to refer to their tag capture.
|
||||
/// 3. catch_or_if_src_node_offset: Ast.Node.Offset, // If inst is switch_block_err_union.
|
||||
/// 4. non_err_info: ProngInfo.NonErr, // If inst is switch_block_err_union.
|
||||
/// 5. else_info: ProngInfo.Else, // If has_else is set.
|
||||
/// 6. under_info: ProngInfo.Under, // If has_under is set and
|
||||
/// // under_is_bare is set.
|
||||
/// 7. under_index: u32, // If has_under is set and
|
||||
/// // under_is_bare is *not* set.
|
||||
/// // Index into switch cases.
|
||||
/// 8. scalar_prong_info: ProngInfo, // for every scalar_cases_len
|
||||
/// 9. multi_prong_info: ProngInfo, // for every multi_cases_len
|
||||
/// 10. multi_case_items_len: u32, // for every multi_cases_len
|
||||
/// 11. multi_case_ranges_len: u32, // If has_ranges is set: for every multi_cases_len
|
||||
/// 12. scalar_item_info: ItemInfo, // for every scalar_cases_len
|
||||
/// 13. multi_items_info: { // for every multi_cases_len
|
||||
/// item_info: ItemInfo, // for each multi_case_items_len
|
||||
/// range_items_info: { // for each multi_case_ranges_len
|
||||
/// first_info: ItemInfo,
|
||||
/// last_info: ItemInfo,
|
||||
/// }
|
||||
/// inst: Index // for every `info.body_len`
|
||||
/// }
|
||||
///
|
||||
/// When analyzing a case body, the switch instruction itself refers to the
|
||||
/// captured error, or to the success value in `non_err_body`. Whether this
|
||||
/// is captured by reference or by value depends on whether the `byref` bit
|
||||
/// is set for the corresponding body. `err_capture_inst` refers to the error
|
||||
/// capture outside of the `switch`, i.e. `err` in
|
||||
/// `x catch |err| switch (err) { ... }`.
|
||||
pub const SwitchBlockErrUnion = struct {
|
||||
operand: Ref,
|
||||
/// 14. non_err_body {
|
||||
/// body_inst: Index // for every non_err_info.body_len
|
||||
/// }
|
||||
/// 15. else_body: { // If has_else is set.
|
||||
/// body_inst: Inst.Index, // for every else_info.body_len
|
||||
/// }
|
||||
/// 16. under_body: { // If has_under is set and
|
||||
/// // under_is_bare is set.
|
||||
/// body_inst: Inst.Index, // for every under_info.body_len
|
||||
/// }
|
||||
/// 17. scalar_bodies: { // for every scalar_cases_len
|
||||
/// prong_body: { // for each body_len in scalar_prong_info
|
||||
/// body_inst: Inst.Index, // for every body_len
|
||||
/// }
|
||||
/// item_body: { // for each body_len in scalar_item_info
|
||||
/// body_inst: Inst.Index, // for every body_len
|
||||
/// }
|
||||
/// }
|
||||
/// 18. multi_bodies: { // for each multi_items_info
|
||||
/// prong_body: {
|
||||
/// body_inst: Inst.Index, // for each multi_prong_info.body_len
|
||||
/// }
|
||||
/// item_body: { // for each item_info
|
||||
/// body_inst: Inst.Index, // for every item_info.body_len
|
||||
/// }
|
||||
/// range_bodies: { // for each .{first_info, last_info} in range_items_info
|
||||
/// first_body_inst: Inst.Index, // for every first_info.body_len
|
||||
/// last_body_inst: Inst.Index, // for every last_info.body_len
|
||||
/// }
|
||||
/// }
|
||||
pub const SwitchBlock = struct {
|
||||
/// Either `catch`/`if` or `switch` operand.
|
||||
raw_operand: Ref,
|
||||
bits: Bits,
|
||||
main_src_node_offset: Ast.Node.Offset,
|
||||
|
||||
pub const Bits = packed struct(u32) {
|
||||
/// If true, one or more prongs have multiple items.
|
||||
has_multi_cases: bool,
|
||||
/// If true, there is an else prong. This is mutually exclusive with `has_under`.
|
||||
/// If true, one or more prongs have ranges.
|
||||
/// Only valid if `has_multi_cases` is also set.
|
||||
any_ranges: bool,
|
||||
has_else: bool,
|
||||
any_uses_err_capture: bool,
|
||||
payload_is_ref: bool,
|
||||
has_under: bool,
|
||||
/// Only valid if `has_under` is also set.
|
||||
under_is_bare: bool,
|
||||
/// If true, at least one prong contains a `continue`.
|
||||
/// Only valid if `has_label` is set.
|
||||
has_continue: bool,
|
||||
// If true, at least one prong has a non-inline payload/tag capture.
|
||||
any_maybe_runtime_capture: bool,
|
||||
payload_capture_inst_is_placeholder: bool,
|
||||
tag_capture_inst_is_placeholder: bool,
|
||||
scalar_cases_len: ScalarCasesLen,
|
||||
|
||||
pub const ScalarCasesLen = u28;
|
||||
// NOTE maybe don't steal any more bits from poor `scalar_cases_len`
|
||||
// and split `Bits` into two parts instead, `raw_operand` surely
|
||||
// wouldn't mind donating a couple of bits for that purpose...
|
||||
pub const ScalarCasesLen = u23;
|
||||
};
|
||||
|
||||
pub const MultiProng = struct {
|
||||
items: []const Ref,
|
||||
body: []const Index,
|
||||
};
|
||||
};
|
||||
|
||||
/// 0. multi_cases_len: u32 // If has_multi_cases 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 special_prong.hasElse() is set.
|
||||
/// info: ProngInfo,
|
||||
/// body member Index for every info.body_len
|
||||
/// }
|
||||
/// 3. under_body { // If special_prong.hasUnder() is set.
|
||||
/// item: Ref, // If special_prong.hasOneAdditionalItem() is set.
|
||||
/// items_len: u32, // If special_prong.hasManyAdditionalItems() is set.
|
||||
/// ranges_len: u32, // If special_prong.hasManyAdditionalItems() is set.
|
||||
/// info: ProngInfo,
|
||||
/// item: Ref, // for every items_len
|
||||
/// ranges: { // for every ranges_len
|
||||
/// item_first: Ref,
|
||||
/// item_last: Ref,
|
||||
/// }
|
||||
/// body member Index for every info.body_len
|
||||
/// }
|
||||
/// 4. scalar_cases: { // for every scalar_cases_len
|
||||
/// item: Ref,
|
||||
/// info: ProngInfo,
|
||||
/// body member Index for every info.body_len
|
||||
/// }
|
||||
/// 5. multi_cases: { // for every multi_cases_len
|
||||
/// items_len: u32,
|
||||
/// ranges_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 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 {
|
||||
/// 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,
|
||||
body_len: u27,
|
||||
capture: ProngInfo.Capture,
|
||||
is_inline: bool,
|
||||
has_tag_capture: bool,
|
||||
is_comptime_unreach: bool,
|
||||
|
||||
pub const Capture = enum(u2) {
|
||||
none,
|
||||
by_val,
|
||||
by_ref,
|
||||
};
|
||||
|
||||
pub const NonErr = packed struct(u32) {
|
||||
body_len: u29,
|
||||
capture: ProngInfo.Capture,
|
||||
operand_is_ref: bool,
|
||||
};
|
||||
|
||||
pub const Else = packed struct(u32) {
|
||||
body_len: u27,
|
||||
capture: ProngInfo.Capture,
|
||||
is_inline: bool,
|
||||
has_tag_capture: bool,
|
||||
is_simple_noreturn: bool,
|
||||
};
|
||||
|
||||
pub const BareUnder = packed struct(u32) {
|
||||
body_len: u29,
|
||||
capture: ProngInfo.Capture,
|
||||
has_tag_capture: bool,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Bits = packed struct(u32) {
|
||||
/// If true, one or more prongs have multiple items.
|
||||
has_multi_cases: bool,
|
||||
/// Information about the special prong.
|
||||
special_prongs: SpecialProngs,
|
||||
/// If true, at least one prong has an inline tag capture.
|
||||
any_has_tag_capture: bool,
|
||||
/// If true, at least one prong has a capture which may not
|
||||
/// be comptime-known via `inline`.
|
||||
any_non_inline_capture: bool,
|
||||
/// If true, at least one prong contains a `continue`.
|
||||
has_continue: bool,
|
||||
scalar_cases_len: ScalarCasesLen,
|
||||
pub const ItemInfo = packed struct(u32) {
|
||||
kind: ItemInfo.Kind,
|
||||
data: u30,
|
||||
|
||||
pub const ScalarCasesLen = u25;
|
||||
pub const Kind = enum(u2) {
|
||||
enum_literal,
|
||||
error_value,
|
||||
number_literal,
|
||||
body_len,
|
||||
};
|
||||
|
||||
pub const Unwrapped = union(ItemInfo.Kind) {
|
||||
enum_literal: Zir.NullTerminatedString,
|
||||
error_value: Zir.NullTerminatedString,
|
||||
number_literal: Inst.Ref,
|
||||
body_len: u32,
|
||||
};
|
||||
|
||||
pub fn wrap(unwrapped: ItemInfo.Unwrapped) ItemInfo {
|
||||
const data_uncasted: u32 = switch (unwrapped) {
|
||||
.enum_literal => |str_index| @intFromEnum(str_index),
|
||||
.error_value => |str_index| @intFromEnum(str_index),
|
||||
.number_literal => |zir_ref| @intFromEnum(zir_ref),
|
||||
.body_len => |body_len| body_len,
|
||||
};
|
||||
return .{ .kind = unwrapped, .data = @intCast(data_uncasted) };
|
||||
}
|
||||
|
||||
pub fn unwrap(item_info: ItemInfo) ItemInfo.Unwrapped {
|
||||
return switch (item_info.kind) {
|
||||
.enum_literal => .{ .enum_literal = @enumFromInt(item_info.data) },
|
||||
.error_value => .{ .error_value = @enumFromInt(item_info.data) },
|
||||
.number_literal => .{ .number_literal = @enumFromInt(item_info.data) },
|
||||
.body_len => .{ .body_len = item_info.data },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn bodyLen(item_info: ItemInfo) ?u32 {
|
||||
return if (item_info.kind == .body_len) item_info.data else null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const MultiProng = struct {
|
||||
items: []const Ref,
|
||||
body: []const Index,
|
||||
};
|
||||
pub const Kind = enum { default, ref, err_union };
|
||||
};
|
||||
|
||||
pub const ArrayInitRefTy = struct {
|
||||
|
|
@ -4004,69 +4031,6 @@ pub const Inst = struct {
|
|||
};
|
||||
};
|
||||
|
||||
pub const SpecialProngs = enum(u3) {
|
||||
none = 0b000,
|
||||
/// Simple `else` prong.
|
||||
/// `else => {},`
|
||||
@"else" = 0b001,
|
||||
/// Simple `_` prong.
|
||||
/// `_ => {},`
|
||||
under = 0b010,
|
||||
/// Both an `else` and a `_` prong.
|
||||
/// `else => {},`
|
||||
/// `_ => {},`
|
||||
under_and_else = 0b011,
|
||||
/// `_` prong with 1 additional item.
|
||||
/// `a, _ => {},`
|
||||
under_one_item = 0b100,
|
||||
/// Both an `else` and a `_` prong with 1 additional item.
|
||||
/// `else => {},`
|
||||
/// `a, _ => {},`
|
||||
under_one_item_and_else = 0b101,
|
||||
/// `_` prong with >1 additional items.
|
||||
/// `a, _, b => {},`
|
||||
under_many_items = 0b110,
|
||||
/// Both an `else` and a `_` prong with >1 additional items.
|
||||
/// `else => {},`
|
||||
/// `a, _, b => {},`
|
||||
under_many_items_and_else = 0b111,
|
||||
|
||||
pub const AdditionalItems = enum(u3) {
|
||||
none = @intFromEnum(SpecialProngs.under),
|
||||
one = @intFromEnum(SpecialProngs.under_one_item),
|
||||
many = @intFromEnum(SpecialProngs.under_many_items),
|
||||
};
|
||||
|
||||
pub fn init(has_else: bool, has_under: bool, additional_items: AdditionalItems) SpecialProngs {
|
||||
const else_bit: u3 = @intFromBool(has_else);
|
||||
const under_bits: u3 = if (has_under)
|
||||
@intFromEnum(additional_items)
|
||||
else
|
||||
@intFromEnum(SpecialProngs.none);
|
||||
return @enumFromInt(else_bit | under_bits);
|
||||
}
|
||||
|
||||
pub fn hasElse(special_prongs: SpecialProngs) bool {
|
||||
return (@intFromEnum(special_prongs) & 0b001) != 0;
|
||||
}
|
||||
|
||||
pub fn hasUnder(special_prongs: SpecialProngs) bool {
|
||||
return (@intFromEnum(special_prongs) & 0b110) != 0;
|
||||
}
|
||||
|
||||
pub fn hasAdditionalItems(special_prongs: SpecialProngs) bool {
|
||||
return (@intFromEnum(special_prongs) & 0b100) != 0;
|
||||
}
|
||||
|
||||
pub fn hasOneAdditionalItem(special_prongs: SpecialProngs) bool {
|
||||
return (@intFromEnum(special_prongs) & 0b110) == @intFromEnum(SpecialProngs.under_one_item);
|
||||
}
|
||||
|
||||
pub fn hasManyAdditionalItems(special_prongs: SpecialProngs) bool {
|
||||
return (@intFromEnum(special_prongs) & 0b110) == @intFromEnum(SpecialProngs.under_many_items);
|
||||
}
|
||||
};
|
||||
|
||||
pub const DeclIterator = struct {
|
||||
extra_index: u32,
|
||||
decls_remaining: u32,
|
||||
|
|
@ -4842,8 +4806,48 @@ fn findTrackableInner(
|
|||
const body = zir.bodySlice(extra.end, extra.data.body_len);
|
||||
try zir.findTrackableBody(gpa, contents, defers, body);
|
||||
},
|
||||
.switch_block, .switch_block_ref => return zir.findTrackableSwitch(gpa, contents, defers, inst, .normal),
|
||||
.switch_block_err_union => return zir.findTrackableSwitch(gpa, contents, defers, inst, .err_union),
|
||||
|
||||
.switch_block,
|
||||
.switch_block_ref,
|
||||
.switch_block_err_union,
|
||||
=> {
|
||||
const zir_switch = zir.getSwitchBlock(inst);
|
||||
if (zir_switch.non_err_case) |non_err_case| {
|
||||
try zir.findTrackableBody(gpa, contents, defers, non_err_case.body);
|
||||
}
|
||||
if (zir_switch.else_case) |else_case| {
|
||||
try zir.findTrackableBody(gpa, contents, defers, else_case.body);
|
||||
}
|
||||
if (zir_switch.under_case.resolve()) |under_case| {
|
||||
try zir.findTrackableBody(gpa, contents, defers, under_case.body);
|
||||
}
|
||||
var extra_index = zir_switch.end;
|
||||
var case_it = zir_switch.iterateCases();
|
||||
while (case_it.next()) |case| {
|
||||
const prong_body = zir.bodySlice(extra_index, case.prong_info.body_len);
|
||||
extra_index += prong_body.len;
|
||||
try zir.findTrackableBody(gpa, contents, defers, prong_body);
|
||||
for (case.item_infos) |item_info| {
|
||||
if (item_info.bodyLen()) |body_len| {
|
||||
const item_body = zir.bodySlice(extra_index, body_len);
|
||||
extra_index += item_body.len;
|
||||
try zir.findTrackableBody(gpa, contents, defers, item_body);
|
||||
}
|
||||
}
|
||||
for (case.range_infos) |range_info| {
|
||||
if (range_info[0].bodyLen()) |body_len| {
|
||||
const first_body = zir.bodySlice(extra_index, body_len);
|
||||
extra_index += first_body.len;
|
||||
try zir.findTrackableBody(gpa, contents, defers, first_body);
|
||||
}
|
||||
if (range_info[1].bodyLen()) |body_len| {
|
||||
const last_body = zir.bodySlice(extra_index, body_len);
|
||||
extra_index += last_body.len;
|
||||
try zir.findTrackableBody(gpa, contents, defers, last_body);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
.suspend_block => @panic("TODO iterate suspend block"),
|
||||
|
||||
|
|
@ -4890,119 +4894,6 @@ fn findTrackableInner(
|
|||
}
|
||||
}
|
||||
|
||||
fn findTrackableSwitch(
|
||||
zir: Zir,
|
||||
gpa: Allocator,
|
||||
contents: *DeclContents,
|
||||
defers: *std.AutoHashMapUnmanaged(u32, void),
|
||||
inst: Inst.Index,
|
||||
/// Distinguishes between `switch_block[_ref]` and `switch_block_err_union`.
|
||||
comptime kind: enum { normal, err_union },
|
||||
) Allocator.Error!void {
|
||||
const inst_data = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node;
|
||||
const extra = zir.extraData(switch (kind) {
|
||||
.normal => Inst.SwitchBlock,
|
||||
.err_union => Inst.SwitchBlockErrUnion,
|
||||
}, inst_data.payload_index);
|
||||
|
||||
var extra_index: usize = extra.end;
|
||||
|
||||
const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
|
||||
const multi_cases_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
break :blk multi_cases_len;
|
||||
} else 0;
|
||||
|
||||
if (switch (kind) {
|
||||
.normal => extra.data.bits.any_has_tag_capture,
|
||||
.err_union => extra.data.bits.any_uses_err_capture,
|
||||
}) {
|
||||
extra_index += 1;
|
||||
}
|
||||
|
||||
const has_special = switch (kind) {
|
||||
.normal => extra.data.bits.special_prongs != .none,
|
||||
.err_union => has_special: {
|
||||
// Handle `non_err_body` first.
|
||||
const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body = zir.bodySlice(extra_index, prong_info.body_len);
|
||||
extra_index += body.len;
|
||||
|
||||
try zir.findTrackableBody(gpa, contents, defers, body);
|
||||
|
||||
break :has_special extra.data.bits.has_else;
|
||||
},
|
||||
};
|
||||
|
||||
if (has_special) {
|
||||
const has_else = if (kind == .normal)
|
||||
extra.data.bits.special_prongs.hasElse()
|
||||
else
|
||||
true;
|
||||
if (has_else) {
|
||||
const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body = zir.bodySlice(extra_index, prong_info.body_len);
|
||||
extra_index += body.len;
|
||||
|
||||
try zir.findTrackableBody(gpa, contents, defers, body);
|
||||
}
|
||||
if (kind == .normal) {
|
||||
const special_prongs = extra.data.bits.special_prongs;
|
||||
|
||||
if (special_prongs.hasUnder()) {
|
||||
var trailing_items_len: u32 = 0;
|
||||
if (special_prongs.hasOneAdditionalItem()) {
|
||||
extra_index += 1;
|
||||
} else if (special_prongs.hasManyAdditionalItems()) {
|
||||
const items_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const ranges_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
trailing_items_len = items_len + ranges_len * 2;
|
||||
}
|
||||
const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
|
||||
extra_index += 1 + trailing_items_len;
|
||||
const body = zir.bodySlice(extra_index, prong_info.body_len);
|
||||
extra_index += body.len;
|
||||
|
||||
try zir.findTrackableBody(gpa, contents, defers, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const scalar_cases_len = extra.data.bits.scalar_cases_len;
|
||||
for (0..scalar_cases_len) |_| {
|
||||
extra_index += 1;
|
||||
const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body = zir.bodySlice(extra_index, prong_info.body_len);
|
||||
extra_index += body.len;
|
||||
|
||||
try zir.findTrackableBody(gpa, contents, defers, body);
|
||||
}
|
||||
}
|
||||
{
|
||||
for (0..multi_cases_len) |_| {
|
||||
const items_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const ranges_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
|
||||
extra_index += items_len + ranges_len * 2;
|
||||
|
||||
const body = zir.bodySlice(extra_index, prong_info.body_len);
|
||||
extra_index += body.len;
|
||||
|
||||
try zir.findTrackableBody(gpa, contents, defers, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn findTrackableBody(
|
||||
zir: Zir,
|
||||
gpa: Allocator,
|
||||
|
|
@ -5337,6 +5228,306 @@ pub fn getAssociatedSrcHash(zir: Zir, inst: Zir.Inst.Index) ?std.zig.SrcHash {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn getSwitchBlock(zir: *const Zir, switch_inst: Inst.Index) UnwrappedSwitchBlock {
|
||||
const has_non_err = switch (zir.instructions.items(.tag)[@intFromEnum(switch_inst)]) {
|
||||
.switch_block, .switch_block_ref => false,
|
||||
.switch_block_err_union => true,
|
||||
else => unreachable,
|
||||
};
|
||||
const inst_data = zir.instructions.items(.data)[@intFromEnum(switch_inst)].pl_node;
|
||||
const extra = zir.extraData(Inst.SwitchBlock, inst_data.payload_index);
|
||||
const bits = extra.data.bits;
|
||||
var extra_index = extra.end;
|
||||
const multi_cases_len = if (bits.has_multi_cases) len: {
|
||||
const multi_cases_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
break :len multi_cases_len;
|
||||
} else 0;
|
||||
const payload_capture_placeholder: Inst.OptionalIndex = if (bits.payload_capture_inst_is_placeholder) inst: {
|
||||
const inst: Inst.Index = @enumFromInt(zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
break :inst inst.toOptional();
|
||||
} else .none;
|
||||
const tag_capture_placeholder: Inst.OptionalIndex = if (bits.tag_capture_inst_is_placeholder) inst: {
|
||||
const inst: Inst.Index = @enumFromInt(zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
break :inst inst.toOptional();
|
||||
} else .none;
|
||||
const catch_or_if_src_node_offset: Ast.Node.OptionalOffset = if (has_non_err) node_offset: {
|
||||
const node_offset: Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(zir.extra[extra_index])));
|
||||
extra_index += 1;
|
||||
break :node_offset node_offset.toOptional();
|
||||
} else .none;
|
||||
const non_err_info: Inst.SwitchBlock.ProngInfo.NonErr = if (has_non_err) non_err_info: {
|
||||
const non_err_info: Inst.SwitchBlock.ProngInfo.NonErr = @bitCast(zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
break :non_err_info non_err_info;
|
||||
} else undefined;
|
||||
const else_info: Inst.SwitchBlock.ProngInfo.Else = if (bits.has_else) else_info: {
|
||||
const else_info: Inst.SwitchBlock.ProngInfo.Else = @bitCast(zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
break :else_info else_info;
|
||||
} else undefined;
|
||||
const bare_under_info: Inst.SwitchBlock.ProngInfo.BareUnder = if (bits.has_under and bits.under_is_bare) bare_under_info: {
|
||||
const bare_under_info: Inst.SwitchBlock.ProngInfo.BareUnder = @bitCast(zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
break :bare_under_info bare_under_info;
|
||||
} else undefined;
|
||||
const under_index: u32 = if (bits.has_under and !bits.under_is_bare) under_index: {
|
||||
const under_index = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
break :under_index under_index;
|
||||
} else undefined;
|
||||
const scalar_cases_len: u32 = bits.scalar_cases_len;
|
||||
const prong_infos: []const Inst.SwitchBlock.ProngInfo =
|
||||
@ptrCast(zir.extra[extra_index..][0 .. scalar_cases_len + multi_cases_len]);
|
||||
extra_index += prong_infos.len;
|
||||
const multi_case_items_lens = zir.extra[extra_index..][0..multi_cases_len];
|
||||
extra_index += multi_case_items_lens.len;
|
||||
const multi_case_ranges_lens: ?[]const u32 = if (bits.any_ranges) lens: {
|
||||
const multi_case_ranges_lens = zir.extra[extra_index..][0..multi_cases_len];
|
||||
extra_index += multi_case_ranges_lens.len;
|
||||
break :lens multi_case_ranges_lens;
|
||||
} else null;
|
||||
var total_items_len: usize = scalar_cases_len;
|
||||
for (multi_case_items_lens) |items_len| {
|
||||
total_items_len += items_len;
|
||||
}
|
||||
if (multi_case_ranges_lens) |ranges_lens| for (ranges_lens) |ranges_len| {
|
||||
total_items_len += 2 * ranges_len;
|
||||
};
|
||||
const item_infos: []const Inst.SwitchBlock.ItemInfo =
|
||||
@ptrCast(zir.extra[extra_index..][0..total_items_len]);
|
||||
extra_index += item_infos.len;
|
||||
const non_err_case: ?UnwrappedSwitchBlock.Case.NonErr = if (has_non_err) non_err_case: {
|
||||
const body = zir.bodySlice(extra_index, non_err_info.body_len);
|
||||
extra_index += body.len;
|
||||
break :non_err_case .{
|
||||
.body = body,
|
||||
.capture = non_err_info.capture,
|
||||
.operand_is_ref = non_err_info.operand_is_ref,
|
||||
};
|
||||
} else null;
|
||||
const else_case: ?UnwrappedSwitchBlock.Case.Else = if (bits.has_else) else_case: {
|
||||
const body = zir.bodySlice(extra_index, else_info.body_len);
|
||||
extra_index += body.len;
|
||||
break :else_case .{
|
||||
.index = .@"else",
|
||||
.body = body,
|
||||
.capture = else_info.capture,
|
||||
.is_inline = else_info.is_inline,
|
||||
.has_tag_capture = else_info.has_tag_capture,
|
||||
.is_simple_noreturn = else_info.is_simple_noreturn,
|
||||
};
|
||||
} else null;
|
||||
const under_case: UnwrappedSwitchBlock.Case.Under = if (bits.has_under) under_case: {
|
||||
if (bits.under_is_bare) {
|
||||
const body = zir.bodySlice(extra_index, bare_under_info.body_len);
|
||||
extra_index += body.len;
|
||||
break :under_case .{ .bare = .{
|
||||
.index = .bare_under,
|
||||
.body = body,
|
||||
.capture = bare_under_info.capture,
|
||||
.has_tag_capture = bare_under_info.has_tag_capture,
|
||||
} };
|
||||
} else {
|
||||
break :under_case .{ .index = under_index };
|
||||
}
|
||||
} else .none;
|
||||
return .{
|
||||
.main_operand = extra.data.raw_operand,
|
||||
.switch_src_node_offset = inst_data.src_node,
|
||||
.catch_or_if_src_node_offset = catch_or_if_src_node_offset,
|
||||
.payload_capture_placeholder = payload_capture_placeholder,
|
||||
.tag_capture_placeholder = tag_capture_placeholder,
|
||||
.has_continue = bits.has_continue,
|
||||
.any_maybe_runtime_capture = bits.any_maybe_runtime_capture,
|
||||
.non_err_case = non_err_case,
|
||||
.else_case = else_case,
|
||||
.under_case = under_case,
|
||||
.prong_infos = prong_infos,
|
||||
.multi_case_items_lens = multi_case_items_lens,
|
||||
.multi_case_ranges_lens = multi_case_ranges_lens,
|
||||
.item_infos = item_infos,
|
||||
.end = extra_index,
|
||||
};
|
||||
}
|
||||
|
||||
/// Trailing (starting at `end`):
|
||||
/// 0. case_bodies: { // for each case in Case.Iterator.next()
|
||||
/// prong_body: {
|
||||
/// body_inst: Inst.Index, // for every case.prong_info.body_len,
|
||||
/// }
|
||||
/// item_body: { // for each body_len in case.item_infos
|
||||
/// body_inst: Inst.Index, // for every body_len
|
||||
/// }
|
||||
/// range_bodies: { // for each .{first_info, last_info} in case.range_infos
|
||||
/// first_body_inst: Inst.Index, // for every first_info.body_len
|
||||
/// last_body_inst: Inst.Index, // for every last_info.body_len
|
||||
/// }
|
||||
/// }
|
||||
pub const UnwrappedSwitchBlock = struct {
|
||||
/// Either `catch`/`if` or `switch` operand.
|
||||
main_operand: Inst.Ref,
|
||||
switch_src_node_offset: Ast.Node.Offset,
|
||||
catch_or_if_src_node_offset: Ast.Node.OptionalOffset,
|
||||
payload_capture_placeholder: Inst.OptionalIndex,
|
||||
tag_capture_placeholder: Inst.OptionalIndex,
|
||||
has_continue: bool,
|
||||
any_maybe_runtime_capture: bool,
|
||||
non_err_case: ?Case.NonErr,
|
||||
else_case: ?Case.Else,
|
||||
under_case: Case.Under,
|
||||
// Refer to doc comment and `iterateCases` to access everything below correctly.
|
||||
prong_infos: []const Inst.SwitchBlock.ProngInfo,
|
||||
multi_case_items_lens: []const u32,
|
||||
multi_case_ranges_lens: ?[]const u32,
|
||||
item_infos: []const Inst.SwitchBlock.ItemInfo,
|
||||
end: usize,
|
||||
|
||||
pub fn anyRanges(unwrapped: *const UnwrappedSwitchBlock) bool {
|
||||
return unwrapped.multi_case_ranges_lens != null;
|
||||
}
|
||||
|
||||
pub fn scalarCasesLen(unwrapped: *const UnwrappedSwitchBlock) u32 {
|
||||
return @intCast(unwrapped.prong_infos.len - unwrapped.multi_case_items_lens.len);
|
||||
}
|
||||
|
||||
pub fn multiCasesLen(unwrapped: *const UnwrappedSwitchBlock) u32 {
|
||||
return @intCast(unwrapped.multi_case_items_lens.len);
|
||||
}
|
||||
|
||||
pub fn totalItemsLen(unwrapped: *const UnwrappedSwitchBlock) u32 {
|
||||
var total_items_len: u32 = @intCast(unwrapped.item_infos.len);
|
||||
if (unwrapped.multi_case_ranges_lens) |ranges_lens| {
|
||||
for (ranges_lens) |len| total_items_len -= len;
|
||||
}
|
||||
return total_items_len;
|
||||
}
|
||||
|
||||
pub const Case = struct {
|
||||
index: Case.Index,
|
||||
prong_info: Inst.SwitchBlock.ProngInfo,
|
||||
item_infos: []const Inst.SwitchBlock.ItemInfo,
|
||||
range_infos: []const [2]Inst.SwitchBlock.ItemInfo,
|
||||
|
||||
pub fn isUnder(case: *const Case) bool {
|
||||
return case.index.is_under;
|
||||
}
|
||||
|
||||
pub const Index = packed struct(u32) {
|
||||
kind: enum(u1) { scalar, multi },
|
||||
is_under: bool,
|
||||
value: u30,
|
||||
|
||||
pub const @"else": Case.Index = .{
|
||||
.kind = .scalar,
|
||||
.is_under = false,
|
||||
.value = std.math.maxInt(u30),
|
||||
};
|
||||
|
||||
pub const bare_under: Case.Index = .{
|
||||
.kind = .scalar,
|
||||
.is_under = true,
|
||||
.value = std.math.maxInt(u30),
|
||||
};
|
||||
};
|
||||
|
||||
pub const NonErr = struct {
|
||||
body: []const Inst.Index,
|
||||
capture: Inst.SwitchBlock.ProngInfo.Capture,
|
||||
operand_is_ref: bool,
|
||||
};
|
||||
|
||||
pub const Else = struct {
|
||||
index: Case.Index,
|
||||
body: []const Inst.Index,
|
||||
capture: Inst.SwitchBlock.ProngInfo.Capture,
|
||||
is_inline: bool,
|
||||
has_tag_capture: bool,
|
||||
is_simple_noreturn: bool,
|
||||
};
|
||||
|
||||
pub const Under = union(enum) {
|
||||
none,
|
||||
bare: Under.Resolved,
|
||||
index: u32,
|
||||
|
||||
pub const Resolved = struct {
|
||||
index: Case.Index,
|
||||
body: []const Inst.Index,
|
||||
capture: Inst.SwitchBlock.ProngInfo.Capture,
|
||||
has_tag_capture: bool,
|
||||
};
|
||||
|
||||
/// If this returns `null` and `under` is not `.none`, you'll have to
|
||||
/// find the under case by iterating all cases and using `isUnder`!
|
||||
pub fn resolve(under: Under) ?Under.Resolved {
|
||||
return switch (under) {
|
||||
.bare => |resolved| resolved,
|
||||
.none, .index => null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Iterator = struct {
|
||||
next_idx: u32,
|
||||
under_idx: ?u32,
|
||||
prong_infos: []const Inst.SwitchBlock.ProngInfo,
|
||||
multi_case_items_lens: []const u32,
|
||||
multi_case_ranges_lens: ?[]const u32,
|
||||
item_infos: []const Inst.SwitchBlock.ItemInfo,
|
||||
|
||||
pub fn next(it: *Iterator) ?Case {
|
||||
const idx = it.next_idx;
|
||||
if (idx == it.prong_infos.len) return null;
|
||||
it.next_idx += 1;
|
||||
const scalar_cases_len = it.prong_infos.len - it.multi_case_items_lens.len;
|
||||
return if (idx < scalar_cases_len) .{
|
||||
.index = .{
|
||||
.kind = .scalar,
|
||||
.is_under = idx == it.under_idx,
|
||||
.value = @intCast(idx),
|
||||
},
|
||||
.prong_info = it.prong_infos[idx],
|
||||
.item_infos = it.itemInfos(1),
|
||||
.range_infos = &.{},
|
||||
} else .{
|
||||
.index = .{
|
||||
.kind = .multi,
|
||||
.is_under = idx == it.under_idx,
|
||||
.value = @intCast(idx - scalar_cases_len),
|
||||
},
|
||||
.prong_info = it.prong_infos[idx],
|
||||
.item_infos = it.itemInfos(it.multi_case_items_lens[idx - scalar_cases_len]),
|
||||
.range_infos = if (it.multi_case_ranges_lens) |ranges_lens| b: {
|
||||
break :b @ptrCast(it.itemInfos(2 * ranges_lens[idx - scalar_cases_len]));
|
||||
} else &.{},
|
||||
};
|
||||
}
|
||||
fn itemInfos(it: *Iterator, count: u32) []const Inst.SwitchBlock.ItemInfo {
|
||||
const lens = it.item_infos[0..count];
|
||||
it.item_infos = it.item_infos[count..];
|
||||
return lens;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub fn iterateCases(unwrapped: UnwrappedSwitchBlock) Case.Iterator {
|
||||
return .{
|
||||
.next_idx = 0,
|
||||
.under_idx = switch (unwrapped.under_case) {
|
||||
.none, .bare => null,
|
||||
.index => |index| index,
|
||||
},
|
||||
.prong_infos = unwrapped.prong_infos,
|
||||
.multi_case_items_lens = unwrapped.multi_case_items_lens,
|
||||
.multi_case_ranges_lens = unwrapped.multi_case_ranges_lens,
|
||||
.item_infos = unwrapped.item_infos,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// When the ZIR update tracking logic must be modified to consider new instructions,
|
||||
/// change this constant to trigger compile errors at all relevant locations.
|
||||
pub const inst_tracking_version = 0;
|
||||
|
|
|
|||
|
|
@ -447,10 +447,9 @@ const Writer = struct {
|
|||
|
||||
.switch_block,
|
||||
.switch_block_ref,
|
||||
.switch_block_err_union,
|
||||
=> try self.writeSwitchBlock(stream, inst),
|
||||
|
||||
.switch_block_err_union => try self.writeSwitchBlockErrUnion(stream, inst),
|
||||
|
||||
.field_ptr_load,
|
||||
.field_ptr,
|
||||
.decl_literal,
|
||||
|
|
@ -1987,322 +1986,150 @@ const Writer = struct {
|
|||
try self.writeSrcNode(stream, inst_data.src_node);
|
||||
}
|
||||
|
||||
fn writeSwitchBlockErrUnion(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
|
||||
const extra = self.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);
|
||||
fn writeSwitchBlock(
|
||||
self: *Writer,
|
||||
stream: *std.Io.Writer,
|
||||
inst: Zir.Inst.Index,
|
||||
) !void {
|
||||
const zir_switch = self.code.getSwitchBlock(inst);
|
||||
var extra_index = zir_switch.end;
|
||||
|
||||
var extra_index: usize = extra.end;
|
||||
|
||||
const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
|
||||
const multi_cases_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
break :blk multi_cases_len;
|
||||
} else 0;
|
||||
|
||||
const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: {
|
||||
const tag_capture_inst = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
break :blk @enumFromInt(tag_capture_inst);
|
||||
} else undefined;
|
||||
|
||||
try self.writeInstRef(stream, extra.data.operand);
|
||||
|
||||
if (extra.data.bits.any_uses_err_capture) {
|
||||
try stream.writeAll(", err_capture=");
|
||||
try self.writeInstIndex(stream, err_capture_inst);
|
||||
}
|
||||
try self.writeInstRef(stream, zir_switch.main_operand);
|
||||
|
||||
self.indent += 2;
|
||||
|
||||
{
|
||||
const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
|
||||
extra_index += 1;
|
||||
|
||||
assert(!info.is_inline);
|
||||
const body = self.code.bodySlice(extra_index, info.body_len);
|
||||
extra_index += body.len;
|
||||
if (zir_switch.non_err_case) |non_err_case| {
|
||||
if (non_err_case.operand_is_ref) try stream.writeAll(" ref");
|
||||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.splatByteAll(' ', self.indent);
|
||||
|
||||
try self.writeSwitchCaptures(stream, non_err_case.capture, false, inst, &zir_switch);
|
||||
|
||||
try stream.writeAll("non_err => ");
|
||||
try self.writeBracedBody(stream, body);
|
||||
try self.writeBracedBody(stream, non_err_case.body);
|
||||
try stream.writeAll(" ");
|
||||
try self.writeSrcNode(stream, zir_switch.catch_or_if_src_node_offset.unwrap().?);
|
||||
}
|
||||
|
||||
if (extra.data.bits.has_else) {
|
||||
const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
|
||||
extra_index += 1;
|
||||
const capture_text = switch (info.capture) {
|
||||
.none => "",
|
||||
.by_val => "by_val ",
|
||||
.by_ref => "by_ref ",
|
||||
};
|
||||
const inline_text = if (info.is_inline) "inline " else "";
|
||||
const body = self.code.bodySlice(extra_index, info.body_len);
|
||||
extra_index += body.len;
|
||||
|
||||
if (zir_switch.else_case) |else_case| {
|
||||
try stream.writeAll(",\n");
|
||||
try stream.splatByteAll(' ', self.indent);
|
||||
try stream.print("{s}{s}else => ", .{ capture_text, inline_text });
|
||||
try self.writeBracedBody(stream, body);
|
||||
|
||||
try self.writeSwitchCaptures(stream, else_case.capture, else_case.has_tag_capture, inst, &zir_switch);
|
||||
if (else_case.is_inline) try stream.writeAll("inline ");
|
||||
|
||||
try stream.writeAll("else => ");
|
||||
try self.writeBracedBody(stream, else_case.body);
|
||||
}
|
||||
if (zir_switch.under_case.resolve()) |under_case| {
|
||||
try stream.writeAll(",\n");
|
||||
try stream.splatByteAll(' ', self.indent);
|
||||
|
||||
try self.writeSwitchCaptures(stream, under_case.capture, under_case.has_tag_capture, inst, &zir_switch);
|
||||
|
||||
try stream.writeAll("_ => ");
|
||||
try self.writeBracedBody(stream, under_case.body);
|
||||
}
|
||||
|
||||
{
|
||||
const scalar_cases_len = extra.data.bits.scalar_cases_len;
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
const item_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
|
||||
extra_index += 1;
|
||||
const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
|
||||
extra_index += 1;
|
||||
const body = self.code.bodySlice(extra_index, info.body_len);
|
||||
extra_index += info.body_len;
|
||||
var case_it = zir_switch.iterateCases();
|
||||
while (case_it.next()) |case| {
|
||||
try stream.writeAll(",\n");
|
||||
try stream.splatByteAll(' ', self.indent);
|
||||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.splatByteAll(' ', self.indent);
|
||||
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);
|
||||
const prong_info = case.prong_info;
|
||||
try self.writeSwitchCaptures(stream, prong_info.capture, prong_info.has_tag_capture, inst, &zir_switch);
|
||||
if (prong_info.is_inline) try stream.writeAll("inline ");
|
||||
|
||||
const prong_body = self.code.bodySlice(extra_index, prong_info.body_len);
|
||||
extra_index += prong_body.len;
|
||||
|
||||
var first_item: bool = true;
|
||||
if (case.isUnder()) {
|
||||
try stream.writeAll("_");
|
||||
first_item = false;
|
||||
}
|
||||
}
|
||||
{
|
||||
var multi_i: usize = 0;
|
||||
while (multi_i < multi_cases_len) : (multi_i += 1) {
|
||||
const items_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const ranges_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
|
||||
extra_index += 1;
|
||||
const items = self.code.refSlice(extra_index, items_len);
|
||||
extra_index += items_len;
|
||||
for (case.item_infos) |item_info| {
|
||||
if (!first_item) try stream.writeAll(", ");
|
||||
first_item = false;
|
||||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.splatByteAll(' ', self.indent);
|
||||
switch (info.capture) {
|
||||
.none => {},
|
||||
.by_val => try stream.writeAll("by_val "),
|
||||
.by_ref => try stream.writeAll("by_ref "),
|
||||
switch (item_info.unwrap()) {
|
||||
.enum_literal => |str_index| {
|
||||
const str = self.code.nullTerminatedString(str_index);
|
||||
try stream.print("\".{f}\"", .{std.zig.fmtString(str)});
|
||||
},
|
||||
.error_value => |str_index| {
|
||||
const str = self.code.nullTerminatedString(str_index);
|
||||
try stream.print("\"error.{f}\"", .{std.zig.fmtString(str)});
|
||||
},
|
||||
.number_literal => |zir_ref| {
|
||||
try self.writeInstRef(stream, zir_ref);
|
||||
},
|
||||
.body_len => |body_len| {
|
||||
const item_body = self.code.bodySlice(extra_index, body_len);
|
||||
extra_index += item_body.len;
|
||||
try self.writeBracedDecl(stream, item_body);
|
||||
},
|
||||
}
|
||||
if (info.is_inline) try stream.writeAll("inline ");
|
||||
}
|
||||
for (case.range_infos) |range_info| {
|
||||
if (!first_item) try stream.writeAll(", ");
|
||||
first_item = false;
|
||||
|
||||
for (items, 0..) |item_ref, item_i| {
|
||||
if (item_i != 0) try stream.writeAll(", ");
|
||||
try self.writeInstRef(stream, item_ref);
|
||||
}
|
||||
var first_range_item = true;
|
||||
for (&range_info) |item_info| {
|
||||
if (!first_range_item) try stream.writeAll("...");
|
||||
first_range_item = false;
|
||||
|
||||
var range_i: usize = 0;
|
||||
while (range_i < ranges_len) : (range_i += 1) {
|
||||
const item_first = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
|
||||
extra_index += 1;
|
||||
const item_last = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
|
||||
extra_index += 1;
|
||||
|
||||
if (range_i != 0 or items.len != 0) {
|
||||
try stream.writeAll(", ");
|
||||
switch (item_info.unwrap()) {
|
||||
.enum_literal => |str_index| {
|
||||
const str = self.code.nullTerminatedString(str_index);
|
||||
try stream.print("\".{f}\"", .{std.zig.fmtString(str)});
|
||||
},
|
||||
.error_value => |str_index| {
|
||||
const str = self.code.nullTerminatedString(str_index);
|
||||
try stream.print("\"error.{f}\"", .{std.zig.fmtString(str)});
|
||||
},
|
||||
.number_literal => |zir_ref| {
|
||||
try self.writeInstRef(stream, zir_ref);
|
||||
},
|
||||
.body_len => |body_len| {
|
||||
const item_body = self.code.bodySlice(extra_index, body_len);
|
||||
extra_index += item_body.len;
|
||||
try self.writeBracedDecl(stream, item_body);
|
||||
},
|
||||
}
|
||||
try self.writeInstRef(stream, item_first);
|
||||
try stream.writeAll("...");
|
||||
try self.writeInstRef(stream, item_last);
|
||||
}
|
||||
|
||||
const body = self.code.bodySlice(extra_index, info.body_len);
|
||||
extra_index += info.body_len;
|
||||
try stream.writeAll(" => ");
|
||||
try self.writeBracedBody(stream, body);
|
||||
}
|
||||
try stream.writeAll(" => ");
|
||||
try self.writeBracedBody(stream, prong_body);
|
||||
}
|
||||
|
||||
self.indent -= 2;
|
||||
|
||||
try stream.writeAll(") ");
|
||||
try self.writeSrcNode(stream, inst_data.src_node);
|
||||
try self.writeSrcNode(stream, zir_switch.switch_src_node_offset);
|
||||
}
|
||||
|
||||
fn writeSwitchBlock(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
|
||||
const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
|
||||
|
||||
var extra_index: usize = extra.end;
|
||||
|
||||
const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
|
||||
const multi_cases_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
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 @enumFromInt(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);
|
||||
fn writeSwitchCaptures(
|
||||
self: *Writer,
|
||||
stream: *std.Io.Writer,
|
||||
capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
|
||||
has_tag_capture: bool,
|
||||
switch_inst: Zir.Inst.Index,
|
||||
zir_switch: *const Zir.UnwrappedSwitchBlock,
|
||||
) !void {
|
||||
if (capture != .none) {
|
||||
try stream.print("{t}=", .{capture});
|
||||
const capture_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
|
||||
try self.writeInstIndex(stream, capture_inst);
|
||||
try stream.writeAll(" ");
|
||||
}
|
||||
|
||||
self.indent += 2;
|
||||
|
||||
const special_prongs = extra.data.bits.special_prongs;
|
||||
|
||||
if (special_prongs.hasElse()) {
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(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.bodySlice(extra_index, info.body_len);
|
||||
extra_index += body.len;
|
||||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.splatByteAll(' ', self.indent);
|
||||
try stream.print("{s}{s}else => ", .{ capture_text, inline_text });
|
||||
try self.writeBracedBody(stream, body);
|
||||
if (has_tag_capture) {
|
||||
try stream.writeAll("tag=");
|
||||
const capture_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst;
|
||||
try self.writeInstIndex(stream, capture_inst);
|
||||
try stream.writeAll(" ");
|
||||
}
|
||||
|
||||
if (special_prongs.hasUnder()) {
|
||||
var single_item_ref: Zir.Inst.Ref = .none;
|
||||
var items_len: u32 = 0;
|
||||
var ranges_len: u32 = 0;
|
||||
if (special_prongs.hasOneAdditionalItem()) {
|
||||
single_item_ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
} else if (special_prongs.hasManyAdditionalItems()) {
|
||||
items_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
ranges_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
}
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(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.splatByteAll(' ', self.indent);
|
||||
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 stream.writeAll("_");
|
||||
if (single_item_ref != .none) {
|
||||
try stream.writeAll(", ");
|
||||
try self.writeInstRef(stream, single_item_ref);
|
||||
}
|
||||
for (items) |item_ref| {
|
||||
try stream.writeAll(", ");
|
||||
try self.writeInstRef(stream, item_ref);
|
||||
}
|
||||
|
||||
var range_i: usize = 0;
|
||||
while (range_i < ranges_len) : (range_i += 1) {
|
||||
const item_first: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const item_last: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
|
||||
try stream.writeAll(", ");
|
||||
try self.writeInstRef(stream, item_first);
|
||||
try stream.writeAll("...");
|
||||
try self.writeInstRef(stream, item_last);
|
||||
}
|
||||
|
||||
const body = self.code.bodySlice(extra_index, info.body_len);
|
||||
extra_index += info.body_len;
|
||||
try stream.writeAll(" => ");
|
||||
try self.writeBracedBody(stream, body);
|
||||
}
|
||||
|
||||
{
|
||||
const scalar_cases_len = extra.data.bits.scalar_cases_len;
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
const item_ref: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body = self.code.bodySlice(extra_index, info.body_len);
|
||||
extra_index += info.body_len;
|
||||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.splatByteAll(' ', self.indent);
|
||||
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);
|
||||
}
|
||||
}
|
||||
{
|
||||
var multi_i: usize = 0;
|
||||
while (multi_i < multi_cases_len) : (multi_i += 1) {
|
||||
const items_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const ranges_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(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.splatByteAll(' ', self.indent);
|
||||
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(", ");
|
||||
try self.writeInstRef(stream, item_ref);
|
||||
}
|
||||
|
||||
var range_i: usize = 0;
|
||||
while (range_i < ranges_len) : (range_i += 1) {
|
||||
const item_first: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const item_last: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
|
||||
if (range_i != 0 or items.len != 0) {
|
||||
try stream.writeAll(", ");
|
||||
}
|
||||
try self.writeInstRef(stream, item_first);
|
||||
try stream.writeAll("...");
|
||||
try self.writeInstRef(stream, item_last);
|
||||
}
|
||||
|
||||
const body = self.code.bodySlice(extra_index, info.body_len);
|
||||
extra_index += info.body_len;
|
||||
try stream.writeAll(" => ");
|
||||
try self.writeBracedBody(stream, body);
|
||||
}
|
||||
}
|
||||
|
||||
self.indent -= 2;
|
||||
|
||||
try stream.writeAll(") ");
|
||||
try self.writeSrcNode(stream, inst_data.src_node);
|
||||
}
|
||||
|
||||
fn writePlNodeField(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue