zir: add switch_block_err_union

This commit is contained in:
dweiller 2023-11-16 21:48:57 +11:00
parent 063d55c504
commit 4136097566
4 changed files with 159 additions and 1 deletions

View File

@ -93,6 +93,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
Zir.Inst.Call.Flags,
Zir.Inst.BuiltinCall.Flags,
Zir.Inst.SwitchBlock.Bits,
Zir.Inst.SwitchBlockErrUnion.Bits,
Zir.Inst.FuncFancy.Bits,
=> @bitCast(@field(extra, field.name)),
@ -2640,6 +2641,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.import,
.switch_block,
.switch_block_ref,
.switch_block_err_union,
.union_init,
.field_type_ref,
.error_set_decl,

View File

@ -1097,6 +1097,7 @@ fn analyzeBodyInner(
.str => try sema.zirStr(inst),
.switch_block => try sema.zirSwitchBlock(block, inst, false),
.switch_block_ref => try sema.zirSwitchBlock(block, inst, true),
.switch_block_err_union => @panic("TODO: implement lowering of switch_block_err_union"),
.type_info => try sema.zirTypeInfo(block, inst),
.size_of => try sema.zirSizeOf(block, inst),
.bit_size_of => try sema.zirBitSizeOf(block, inst),

View File

@ -100,6 +100,7 @@ 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,
=> @bitCast(code.extra[i]),
@ -685,6 +686,9 @@ 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`.
switch_block_err_union,
/// Check that operand type supports the dereference operand (.*).
/// Uses the `un_node` field.
validate_deref,
@ -1186,6 +1190,7 @@ pub const Inst = struct {
.set_eval_branch_quota,
.switch_block,
.switch_block_ref,
.switch_block_err_union,
.validate_deref,
.validate_destructure,
.union_init,
@ -1483,6 +1488,7 @@ pub const Inst = struct {
.typeof_log2_int_type,
.switch_block,
.switch_block_ref,
.switch_block_err_union,
.union_init,
.field_type_ref,
.enum_from_int,
@ -1735,6 +1741,7 @@ pub const Inst = struct {
.enum_literal = .str_tok,
.switch_block = .pl_node,
.switch_block_ref = .pl_node,
.switch_block_err_union = .pl_node,
.validate_deref = .un_node,
.validate_destructure = .pl_node,
.field_type_ref = .pl_node,
@ -2776,6 +2783,26 @@ pub const Inst = struct {
index: u32,
};
pub const SwitchBlockErrUnion = struct {
operand: Ref,
bits: Bits,
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`.
has_else: bool,
scalar_cases_len: ScalarCasesLen,
pub const ScalarCasesLen = u30;
};
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 has_else or has_under is set.
@ -2824,7 +2851,7 @@ pub const Inst = struct {
};
};
pub const Bits = packed struct {
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`.

View File

@ -464,6 +464,8 @@ const Writer = struct {
.switch_block_ref,
=> try self.writeSwitchBlock(stream, inst),
.switch_block_err_union => try self.writeSwitchBlockErrUnion(stream, inst),
.field_val,
.field_ptr,
=> try self.writePlNodeField(stream, inst),
@ -2026,6 +2028,132 @@ const Writer = struct {
try self.writeSrc(stream, inst_data.src());
}
fn writeSwitchBlockErrUnion(self: *Writer, stream: anytype, 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);
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;
try self.writeInstRef(stream, extra.data.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;
try stream.writeAll(",\n");
try stream.writeByteNTimes(' ', self.indent);
try stream.writeAll("non_err => ");
try self.writeBracedBody(stream, body);
}
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;
try stream.writeAll(",\n");
try stream.writeByteNTimes(' ', self.indent);
try stream.print("{s}{s}else => ", .{ capture_text, inline_text });
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 = @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;
try stream.writeAll(",\n");
try stream.writeByteNTimes(' ', 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 = @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;
try stream.writeAll(",\n");
try stream.writeByteNTimes(' ', 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 = @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(", ");
}
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.writeSrc(stream, inst_data.src());
}
fn writeSwitchBlock(self: *Writer, stream: anytype, 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);