authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-17 20:45:55-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-17 20:45:55-07:00
log84c2c47fae82e913286a2306d8947252ae3a42f7
treea57dc1f9a211f3852496988ae1e09a222e9d6f29
parent2600978a9ddc295c347fd6ffe7c6ba20931b956e

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.

8 files changed, 71 insertions(+), 61 deletions(-)

src/AstGen.zig+9-8
...@@ -2198,8 +2198,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2198,8 +2198,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2198 .switch_capture_ref,2198 .switch_capture_ref,
2199 .switch_capture_multi,2199 .switch_capture_multi,
2200 .switch_capture_multi_ref,2200 .switch_capture_multi_ref,
2201 .switch_capture_else,
2202 .switch_capture_else_ref,
2203 .struct_init_empty,2201 .struct_init_empty,
2204 .struct_init,2202 .struct_init,
2205 .struct_init_ref,2203 .struct_init_ref,
...@@ -5758,16 +5756,19 @@ fn switchExpr(...@@ -5758,16 +5756,19 @@ fn switchExpr(
5758 }5756 }
5759 if (case_node == special_node) {5757 if (case_node == special_node) {
5760 const capture_tag: Zir.Inst.Tag = if (is_ptr)5758 const capture_tag: Zir.Inst.Tag = if (is_ptr)
5761 .switch_capture_else_ref5759 .switch_capture_ref
5762 else5760 else
5763 .switch_capture_else;5761 .switch_capture;
5764 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);5762 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
5765 try astgen.instructions.append(gpa, .{5763 try astgen.instructions.append(gpa, .{
5766 .tag = capture_tag,5764 .tag = capture_tag,
5767 .data = .{ .switch_capture = .{5765 .data = .{
5768 .switch_inst = switch_block,5766 .switch_capture = .{
5769 .prong_index = undefined,5767 .switch_inst = switch_block,
5770 } },5768 // Max int communicates that this is the else/underscore prong.
5769 .prong_index = std.math.maxInt(u32),
5770 },
5771 },
5771 });5772 });
5772 } else {5773 } else {
5773 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);5774 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
src/Sema.zig+21-22
...@@ -669,8 +669,6 @@ fn analyzeBodyInner(...@@ -669,8 +669,6 @@ fn analyzeBodyInner(
669 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),669 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
670 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),670 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
671 .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),671 .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
672 .switch_capture_else => try sema.zirSwitchCaptureElse(block, inst, false),
673 .switch_capture_else_ref => try sema.zirSwitchCaptureElse(block, inst, true),
674 .type_info => try sema.zirTypeInfo(block, inst),672 .type_info => try sema.zirTypeInfo(block, inst),
675 .size_of => try sema.zirSizeOf(block, inst),673 .size_of => try sema.zirSizeOf(block, inst),
676 .bit_size_of => try sema.zirBitSizeOf(block, inst),674 .bit_size_of => try sema.zirBitSizeOf(block, inst),
...@@ -6071,6 +6069,27 @@ fn zirSwitchCapture(...@@ -6071,6 +6069,27 @@ fn zirSwitchCapture(
6071 const operand_ptr_ty = sema.typeOf(operand_ptr);6069 const operand_ptr_ty = sema.typeOf(operand_ptr);
6072 const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;6070 const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
60736071
6072 if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {
6073 // It is the else/`_` prong.
6074 switch (operand_ty.zigTypeTag()) {
6075 .ErrorSet => {
6076 return sema.fail(block, operand_src, "TODO implement Sema for zirSwitchCaptureElse for error sets", .{});
6077 },
6078 else => {},
6079 }
6080 if (is_ref) {
6081 assert(operand_is_ref);
6082 return operand_ptr;
6083 }
6084
6085 const operand = if (operand_is_ref)
6086 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
6087 else
6088 operand_ptr;
6089
6090 return operand;
6091 }
6092
6074 if (is_multi) {6093 if (is_multi) {
6075 return sema.fail(block, switch_src, "TODO implement Sema for switch capture multi", .{});6094 return sema.fail(block, switch_src, "TODO implement Sema for switch capture multi", .{});
6076 }6095 }
...@@ -6137,26 +6156,6 @@ fn zirSwitchCapture(...@@ -6137,26 +6156,6 @@ fn zirSwitchCapture(
6137 }6156 }
6138}6157}
61396158
6140fn zirSwitchCaptureElse(
6141 sema: *Sema,
6142 block: *Block,
6143 inst: Zir.Inst.Index,
6144 is_ref: bool,
6145) CompileError!Air.Inst.Ref {
6146 const tracy = trace(@src());
6147 defer tracy.end();
6148
6149 const zir_datas = sema.code.instructions.items(.data);
6150 const capture_info = zir_datas[inst].switch_capture;
6151 const switch_info = zir_datas[capture_info.switch_inst].pl_node;
6152 const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index).data;
6153 const src = switch_info.src();
6154 const operand_is_ref = switch_extra.bits.is_ref;
6155 assert(!is_ref or operand_is_ref);
6156
6157 return sema.fail(block, src, "TODO implement Sema for zirSwitchCaptureElse", .{});
6158}
6159
6160fn zirSwitchCond(6159fn zirSwitchCond(
6161 sema: *Sema,6160 sema: *Sema,
6162 block: *Block,6161 block: *Block,
src/Zir.zig+9-11
...@@ -625,10 +625,14 @@ pub const Inst = struct {...@@ -625,10 +625,14 @@ pub const Inst = struct {
625 switch_cond_ref,625 switch_cond_ref,
626 /// Produces the capture value for a switch prong.626 /// Produces the capture value for a switch prong.
627 /// Uses the `switch_capture` field.627 /// Uses the `switch_capture` field.
628 /// If the `prong_index` field is max int, it means this is the capture
629 /// for the else/`_` prong.
628 switch_capture,630 switch_capture,
629 /// Produces the capture value for a switch prong.631 /// Produces the capture value for a switch prong.
630 /// Result is a pointer to the value.632 /// Result is a pointer to the value.
631 /// Uses the `switch_capture` field.633 /// Uses the `switch_capture` field.
634 /// If the `prong_index` field is max int, it means this is the capture
635 /// for the else/`_` prong.
632 switch_capture_ref,636 switch_capture_ref,
633 /// Produces the capture value for a switch prong.637 /// Produces the capture value for a switch prong.
634 /// The prong is one of the multi cases.638 /// The prong is one of the multi cases.
...@@ -639,13 +643,6 @@ pub const Inst = struct {...@@ -639,13 +643,6 @@ pub const Inst = struct {
639 /// Result is a pointer to the value.643 /// Result is a pointer to the value.
640 /// Uses the `switch_capture` field.644 /// Uses the `switch_capture` field.
641 switch_capture_multi_ref,645 switch_capture_multi_ref,
642 /// Produces the capture value for the else/'_' switch prong.
643 /// Uses the `switch_capture` field.
644 switch_capture_else,
645 /// Produces the capture value for the else/'_' switch prong.
646 /// Result is a pointer to the value.
647 /// Uses the `switch_capture` field.
648 switch_capture_else_ref,
649 /// Given a set of `field_ptr` instructions, assumes they are all part of a struct646 /// Given a set of `field_ptr` instructions, assumes they are all part of a struct
650 /// initialization expression, and emits compile errors for duplicate fields647 /// initialization expression, and emits compile errors for duplicate fields
651 /// as well as missing fields, if applicable.648 /// as well as missing fields, if applicable.
...@@ -1082,8 +1079,6 @@ pub const Inst = struct {...@@ -1082,8 +1079,6 @@ pub const Inst = struct {
1082 .switch_capture_ref,1079 .switch_capture_ref,
1083 .switch_capture_multi,1080 .switch_capture_multi,
1084 .switch_capture_multi_ref,1081 .switch_capture_multi_ref,
1085 .switch_capture_else,
1086 .switch_capture_else_ref,
1087 .switch_block,1082 .switch_block,
1088 .switch_cond,1083 .switch_cond,
1089 .switch_cond_ref,1084 .switch_cond_ref,
...@@ -1340,8 +1335,6 @@ pub const Inst = struct {...@@ -1340,8 +1335,6 @@ pub const Inst = struct {
1340 .switch_capture_ref = .switch_capture,1335 .switch_capture_ref = .switch_capture,
1341 .switch_capture_multi = .switch_capture,1336 .switch_capture_multi = .switch_capture,
1342 .switch_capture_multi_ref = .switch_capture,1337 .switch_capture_multi_ref = .switch_capture,
1343 .switch_capture_else = .switch_capture,
1344 .switch_capture_else_ref = .switch_capture,
1345 .validate_struct_init = .pl_node,1338 .validate_struct_init = .pl_node,
1346 .validate_struct_init_comptime = .pl_node,1339 .validate_struct_init_comptime = .pl_node,
1347 .validate_array_init = .pl_node,1340 .validate_array_init = .pl_node,
...@@ -1469,6 +1462,11 @@ pub const Inst = struct {...@@ -1469,6 +1462,11 @@ pub const Inst = struct {
1469 .extended = .extended,1462 .extended = .extended,
1470 });1463 });
1471 };1464 };
1465
1466 // Uncomment to view how many tag slots are available.
1467 //comptime {
1468 // @compileLog("ZIR tags left: ", 256 - @typeInfo(Tag).Enum.fields.len);
1469 //}
1472 };1470 };
14731471
1474 /// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum.1472 /// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum.
src/print_zir.zig-2
...@@ -425,8 +425,6 @@ const Writer = struct {...@@ -425,8 +425,6 @@ const Writer = struct {
425 .switch_capture_ref,425 .switch_capture_ref,
426 .switch_capture_multi,426 .switch_capture_multi,
427 .switch_capture_multi_ref,427 .switch_capture_multi_ref,
428 .switch_capture_else,
429 .switch_capture_else_ref,
430 => try self.writeSwitchCapture(stream, inst),428 => try self.writeSwitchCapture(stream, inst),
431429
432 .dbg_stmt => try self.writeDbgStmt(stream, inst),430 .dbg_stmt => try self.writeDbgStmt(stream, inst),
test/behavior.zig-1
...@@ -203,7 +203,6 @@ test {...@@ -203,7 +203,6 @@ test {
203 if (builtin.target.cpu.arch == .wasm32) {203 if (builtin.target.cpu.arch == .wasm32) {
204 _ = @import("behavior/wasm.zig");204 _ = @import("behavior/wasm.zig");
205 }205 }
206 _ = @import("behavior/while_stage1.zig");
207 _ = @import("behavior/translate_c_macros_stage1.zig");206 _ = @import("behavior/translate_c_macros_stage1.zig");
208 }207 }
209 }208 }
test/behavior/switch.zig+15
...@@ -359,6 +359,21 @@ fn return_a_number() anyerror!i32 {...@@ -359,6 +359,21 @@ fn return_a_number() anyerror!i32 {
359 return 1;359 return 1;
360}360}
361361
362test "switch on integer with else capturing expr" {
363 const S = struct {
364 fn doTheTest() !void {
365 var x: i32 = 5;
366 switch (x + 10) {
367 14 => @panic("fail"),
368 16 => @panic("fail"),
369 else => |e| try expect(e == 15),
370 }
371 }
372 };
373 try S.doTheTest();
374 comptime try S.doTheTest();
375}
376
362test "else prong of switch on error set excludes other cases" {377test "else prong of switch on error set excludes other cases" {
363 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO378 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
364379
test/behavior/while.zig+17
...@@ -266,3 +266,20 @@ test "while optional 2 break statements and an else" {...@@ -266,3 +266,20 @@ test "while optional 2 break statements and an else" {
266 try S.entry(true, false);266 try S.entry(true, false);
267 comptime try S.entry(true, false);267 comptime try S.entry(true, false);
268}268}
269
270test "while error 2 break statements and an else" {
271 if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO
272
273 const S = struct {
274 fn entry(opt_t: anyerror!bool, f: bool) !void {
275 var ok = false;
276 ok = while (opt_t) |t| {
277 if (f) break false;
278 if (t) break true;
279 } else |_| false;
280 try expect(ok);
281 }
282 };
283 try S.entry(true, false);
284 comptime try S.entry(true, false);
285}
test/behavior/while_stage1.zig deleted-17
...@@ -1,17 +0,0 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "while error 2 break statements and an else" {
5 const S = struct {
6 fn entry(opt_t: anyerror!bool, f: bool) !void {
7 var ok = false;
8 ok = while (opt_t) |t| {
9 if (f) break false;
10 if (t) break true;
11 } else |_| false;
12 try expect(ok);
13 }
14 };
15 try S.entry(true, false);
16 comptime try S.entry(true, false);
17}