Commit 5ff518fbb9

Veikka Tuominen <git@vexu.eu>
2022-03-25 12:21:22
Sema: implement zirSwitchCapture for error sets
1 parent 26dfbf8
Changed files (3)
src
test
behavior
src/Sema.zig
@@ -6938,7 +6938,6 @@ fn zirSwitchCapture(
     const switch_info = zir_datas[capture_info.switch_inst].pl_node;
     const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);
     const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };
-    const switch_src = switch_info.src();
     const operand_is_ref = switch_extra.data.bits.is_ref;
     const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;
     const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node;
@@ -6965,7 +6964,29 @@ fn zirSwitchCapture(
     }
 
     if (is_multi) {
-        return sema.fail(block, switch_src, "TODO implement Sema for switch capture multi", .{});
+        const items = switch_extra.data.getMultiProng(sema.code, switch_extra.end, capture_info.prong_index).items;
+
+        var names: Module.ErrorSet.NameMap = .{};
+        try names.ensureUnusedCapacity(sema.arena, items.len);
+        for (items) |item| {
+            const item_ref = sema.resolveInst(item);
+            // Previous switch validation ensured this will succeed
+            const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable;
+            names.putAssumeCapacityNoClobber(
+                item_val.getError().?,
+                {},
+            );
+        }
+
+        // names must be sorted
+        Module.ErrorSet.sortNames(&names);
+        const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names);
+
+        const operand = if (operand_is_ref)
+            try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
+        else
+            operand_ptr;
+        return sema.bitCast(block, else_error_ty, operand, operand_src);
     }
     const scalar_prong = switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index);
     const item = sema.resolveInst(scalar_prong.item);
@@ -7022,7 +7043,12 @@ fn zirSwitchCapture(
             return block.addStructFieldVal(operand, field_index, field.ty);
         },
         .ErrorSet => {
-            return sema.fail(block, operand_src, "TODO implement Sema for zirSwitchCapture for error sets", .{});
+            const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?);
+            const operand = if (operand_is_ref)
+                try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
+            else
+                operand_ptr;
+            return sema.bitCast(block, item_ty, operand, operand_src);
         },
         else => {
             return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{
src/Zir.zig
@@ -2620,6 +2620,53 @@ pub const Inst = struct {
                 };
             }
         }
+
+        pub const MultiProng = struct {
+            items: []const Ref,
+            body: []const Index,
+        };
+
+        pub fn getMultiProng(
+            self: SwitchBlock,
+            zir: Zir,
+            extra_end: usize,
+            prong_index: usize,
+        ) MultiProng {
+            // +1 for self.bits.has_multi_cases == true
+            var extra_index: usize = extra_end + 1;
+
+            if (self.bits.specialProng() != .none) {
+                const body_len = zir.extra[extra_index];
+                extra_index += 1;
+                const body = zir.extra[extra_index..][0..body_len];
+                extra_index += body.len;
+            }
+
+            var scalar_i: usize = 0;
+            while (scalar_i < self.bits.scalar_cases_len) : (scalar_i += 1) {
+                extra_index += 1;
+                const body_len = zir.extra[extra_index];
+                extra_index += 1;
+                extra_index += body_len;
+            }
+            var multi_i: u32 = 0;
+            while (true) : (multi_i += 1) {
+                const items_len = zir.extra[extra_index];
+                extra_index += 2;
+                const body_len = zir.extra[extra_index];
+                extra_index += 1;
+                const items = zir.refSlice(extra_index, items_len);
+                extra_index += items_len;
+                const body = zir.extra[extra_index..][0..body_len];
+                extra_index += body_len;
+
+                if (multi_i < prong_index) continue;
+                return .{
+                    .items = items,
+                    .body = body,
+                };
+            }
+        }
     };
 
     pub const Field = struct {
test/behavior/switch.zig
@@ -465,7 +465,10 @@ test "else prong of switch on error set excludes other cases" {
 }
 
 test "switch prongs with error set cases make a new error set type for capture value" {
-    if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
 
     const S = struct {
         fn doTheTest() !void {