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:
@@ -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);
|
||||
|
||||
43
src/Sema.zig
43
src/Sema.zig
@@ -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,
|
||||
|
||||
20
src/Zir.zig
20
src/Zir.zig
@@ -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.
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user