Commit 4136097566

dweiller <4678790+dweiller@users.noreply.github.com>
2023-11-16 11:48:57
zir: add switch_block_err_union
1 parent 063d55c
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,
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);
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),
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`.