diff --git a/src/AstGen.zig b/src/AstGen.zig index c4fd31d81..f9b53c3de 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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, diff --git a/src/Sema.zig b/src/Sema.zig index 8f637d884..8cc1fad38 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -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), diff --git a/src/Zir.zig b/src/Zir.zig index 6c7f9711c..e49a888b9 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -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`. diff --git a/src/print_zir.zig b/src/print_zir.zig index df11c19ac..939d1779e 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -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);