Sema: implement zirSwitchCapture for error sets

This commit is contained in:
Veikka Tuominen 2022-03-25 13:21:22 +02:00
parent 26dfbf8122
commit 5ff518fbb9
3 changed files with 80 additions and 4 deletions

View File

@ -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", .{

View File

@ -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 {

View File

@ -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 {