Sema: implement else capture value

The ZIR instructions `switch_capture_else` and `switch_capture_ref` are
removed because they are not needed. Instead, the prong index is set to
max int for the special prong.

Else prong with error sets is not handled yet.

Adds a new behavior test because there was not a prior on to cover only
the capture value of else on a switch.
This commit is contained in:
Andrew Kelley
2022-01-17 20:45:55 -07:00
parent 2600978a9d
commit 84c2c47fae
8 changed files with 71 additions and 61 deletions

View File

@@ -2198,8 +2198,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
.switch_capture_ref,
.switch_capture_multi,
.switch_capture_multi_ref,
.switch_capture_else,
.switch_capture_else_ref,
.struct_init_empty,
.struct_init,
.struct_init_ref,
@@ -5758,16 +5756,19 @@ fn switchExpr(
}
if (case_node == special_node) {
const capture_tag: Zir.Inst.Tag = if (is_ptr)
.switch_capture_else_ref
.switch_capture_ref
else
.switch_capture_else;
.switch_capture;
capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
try astgen.instructions.append(gpa, .{
.tag = capture_tag,
.data = .{ .switch_capture = .{
.switch_inst = switch_block,
.prong_index = undefined,
} },
.data = .{
.switch_capture = .{
.switch_inst = switch_block,
// Max int communicates that this is the else/underscore prong.
.prong_index = std.math.maxInt(u32),
},
},
});
} else {
const is_multi_case_bits: u2 = @boolToInt(is_multi_case);

View File

@@ -669,8 +669,6 @@ fn analyzeBodyInner(
.switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
.switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
.switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
.switch_capture_else => try sema.zirSwitchCaptureElse(block, inst, false),
.switch_capture_else_ref => try sema.zirSwitchCaptureElse(block, inst, true),
.type_info => try sema.zirTypeInfo(block, inst),
.size_of => try sema.zirSizeOf(block, inst),
.bit_size_of => try sema.zirBitSizeOf(block, inst),
@@ -6071,6 +6069,27 @@ fn zirSwitchCapture(
const operand_ptr_ty = sema.typeOf(operand_ptr);
const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {
// It is the else/`_` prong.
switch (operand_ty.zigTypeTag()) {
.ErrorSet => {
return sema.fail(block, operand_src, "TODO implement Sema for zirSwitchCaptureElse for error sets", .{});
},
else => {},
}
if (is_ref) {
assert(operand_is_ref);
return operand_ptr;
}
const operand = if (operand_is_ref)
try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
else
operand_ptr;
return operand;
}
if (is_multi) {
return sema.fail(block, switch_src, "TODO implement Sema for switch capture multi", .{});
}
@@ -6137,26 +6156,6 @@ fn zirSwitchCapture(
}
}
fn zirSwitchCaptureElse(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
is_ref: bool,
) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
const zir_datas = sema.code.instructions.items(.data);
const capture_info = zir_datas[inst].switch_capture;
const switch_info = zir_datas[capture_info.switch_inst].pl_node;
const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index).data;
const src = switch_info.src();
const operand_is_ref = switch_extra.bits.is_ref;
assert(!is_ref or operand_is_ref);
return sema.fail(block, src, "TODO implement Sema for zirSwitchCaptureElse", .{});
}
fn zirSwitchCond(
sema: *Sema,
block: *Block,

View File

@@ -625,10 +625,14 @@ pub const Inst = struct {
switch_cond_ref,
/// Produces the capture value for a switch prong.
/// Uses the `switch_capture` field.
/// If the `prong_index` field is max int, it means this is the capture
/// for the else/`_` prong.
switch_capture,
/// Produces the capture value for a switch prong.
/// Result is a pointer to the value.
/// Uses the `switch_capture` field.
/// If the `prong_index` field is max int, it means this is the capture
/// for the else/`_` prong.
switch_capture_ref,
/// Produces the capture value for a switch prong.
/// The prong is one of the multi cases.
@@ -639,13 +643,6 @@ pub const Inst = struct {
/// Result is a pointer to the value.
/// Uses the `switch_capture` field.
switch_capture_multi_ref,
/// Produces the capture value for the else/'_' switch prong.
/// Uses the `switch_capture` field.
switch_capture_else,
/// Produces the capture value for the else/'_' switch prong.
/// Result is a pointer to the value.
/// Uses the `switch_capture` field.
switch_capture_else_ref,
/// Given a set of `field_ptr` instructions, assumes they are all part of a struct
/// initialization expression, and emits compile errors for duplicate fields
/// as well as missing fields, if applicable.
@@ -1082,8 +1079,6 @@ pub const Inst = struct {
.switch_capture_ref,
.switch_capture_multi,
.switch_capture_multi_ref,
.switch_capture_else,
.switch_capture_else_ref,
.switch_block,
.switch_cond,
.switch_cond_ref,
@@ -1340,8 +1335,6 @@ pub const Inst = struct {
.switch_capture_ref = .switch_capture,
.switch_capture_multi = .switch_capture,
.switch_capture_multi_ref = .switch_capture,
.switch_capture_else = .switch_capture,
.switch_capture_else_ref = .switch_capture,
.validate_struct_init = .pl_node,
.validate_struct_init_comptime = .pl_node,
.validate_array_init = .pl_node,
@@ -1469,6 +1462,11 @@ pub const Inst = struct {
.extended = .extended,
});
};
// Uncomment to view how many tag slots are available.
//comptime {
// @compileLog("ZIR tags left: ", 256 - @typeInfo(Tag).Enum.fields.len);
//}
};
/// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum.

View File

@@ -425,8 +425,6 @@ const Writer = struct {
.switch_capture_ref,
.switch_capture_multi,
.switch_capture_multi_ref,
.switch_capture_else,
.switch_capture_else_ref,
=> try self.writeSwitchCapture(stream, inst),
.dbg_stmt => try self.writeDbgStmt(stream, inst),

View File

@@ -203,7 +203,6 @@ test {
if (builtin.target.cpu.arch == .wasm32) {
_ = @import("behavior/wasm.zig");
}
_ = @import("behavior/while_stage1.zig");
_ = @import("behavior/translate_c_macros_stage1.zig");
}
}

View File

@@ -359,6 +359,21 @@ fn return_a_number() anyerror!i32 {
return 1;
}
test "switch on integer with else capturing expr" {
const S = struct {
fn doTheTest() !void {
var x: i32 = 5;
switch (x + 10) {
14 => @panic("fail"),
16 => @panic("fail"),
else => |e| try expect(e == 15),
}
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "else prong of switch on error set excludes other cases" {
if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO

View File

@@ -266,3 +266,20 @@ test "while optional 2 break statements and an else" {
try S.entry(true, false);
comptime try S.entry(true, false);
}
test "while error 2 break statements and an else" {
if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO
const S = struct {
fn entry(opt_t: anyerror!bool, f: bool) !void {
var ok = false;
ok = while (opt_t) |t| {
if (f) break false;
if (t) break true;
} else |_| false;
try expect(ok);
}
};
try S.entry(true, false);
comptime try S.entry(true, false);
}

View File

@@ -1,17 +0,0 @@
const std = @import("std");
const expect = std.testing.expect;
test "while error 2 break statements and an else" {
const S = struct {
fn entry(opt_t: anyerror!bool, f: bool) !void {
var ok = false;
ok = while (opt_t) |t| {
if (f) break false;
if (t) break true;
} else |_| false;
try expect(ok);
}
};
try S.entry(true, false);
comptime try S.entry(true, false);
}