From 84c2c47fae82e913286a2306d8947252ae3a42f7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 17 Jan 2022 20:45:55 -0700 Subject: [PATCH] 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. --- src/AstGen.zig | 17 +++++++------- src/Sema.zig | 43 +++++++++++++++++----------------- src/Zir.zig | 20 +++++++--------- src/print_zir.zig | 2 -- test/behavior.zig | 1 - test/behavior/switch.zig | 15 ++++++++++++ test/behavior/while.zig | 17 ++++++++++++++ test/behavior/while_stage1.zig | 17 -------------- 8 files changed, 71 insertions(+), 61 deletions(-) delete mode 100644 test/behavior/while_stage1.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 32efc8305f..d546eb7bef 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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); diff --git a/src/Sema.zig b/src/Sema.zig index 9978e5f1a7..82af87bd4d 100644 --- a/src/Sema.zig +++ b/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, diff --git a/src/Zir.zig b/src/Zir.zig index a1b5fa2188..96f85b73df 100644 --- a/src/Zir.zig +++ b/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. diff --git a/src/print_zir.zig b/src/print_zir.zig index e367cf056d..6cbef5d446 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -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), diff --git a/test/behavior.zig b/test/behavior.zig index 811050445c..fe73529810 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -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"); } } diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 0b43876869..07b6c4b9f1 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.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 diff --git a/test/behavior/while.zig b/test/behavior/while.zig index d86e061d17..15dd9ee54c 100644 --- a/test/behavior/while.zig +++ b/test/behavior/while.zig @@ -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); +} diff --git a/test/behavior/while_stage1.zig b/test/behavior/while_stage1.zig deleted file mode 100644 index 9fee032b81..0000000000 --- a/test/behavior/while_stage1.zig +++ /dev/null @@ -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); -}