authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-31 00:33:45+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-09-01 18:31:01+01:00
logb7a55cd6c3ca0c4c97f266b72f741b980416456a
treeb164dd3a629abb7d1ca3aaa697712b8817e684cc
parentfd70d9db9960a98fb97def91aa34f56c15499ebf
signature Commit is signed but in an unrecognized format.

AstGen: allow breaking from labeled switch

Also, don't use the special switch lowering for errors if the switch is labeled; this isn't currently supported. Related: #20627.

2 files changed, 38 insertions(+), 21 deletions(-)

lib/std/zig/AstGen.zig+14-21
...@@ -857,13 +857,10 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -857,13 +857,10 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
857 const if_full = tree.fullIf(node).?;857 const if_full = tree.fullIf(node).?;
858 no_switch_on_err: {858 no_switch_on_err: {
859 const error_token = if_full.error_token orelse break :no_switch_on_err;859 const error_token = if_full.error_token orelse break :no_switch_on_err;
860 switch (node_tags[if_full.ast.else_expr]) {860 const full_switch = tree.fullSwitch(if_full.ast.else_expr) orelse break :no_switch_on_err;
861 .@"switch", .switch_comma => {},861 if (full_switch.label_token != null) break :no_switch_on_err;
862 else => break :no_switch_on_err,862 if (node_tags[full_switch.ast.condition] != .identifier) break :no_switch_on_err;
863 }863 if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(main_tokens[full_switch.ast.condition]))) break :no_switch_on_err;
864 const switch_operand = node_datas[if_full.ast.else_expr].lhs;
865 if (node_tags[switch_operand] != .identifier) break :no_switch_on_err;
866 if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(main_tokens[switch_operand]))) break :no_switch_on_err;
867 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if");864 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if");
868 }865 }
869 return ifExpr(gz, scope, ri.br(), node, if_full);866 return ifExpr(gz, scope, ri.br(), node, if_full);
...@@ -1049,13 +1046,10 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -1049,13 +1046,10 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
1049 null;1046 null;
1050 no_switch_on_err: {1047 no_switch_on_err: {
1051 const capture_token = payload_token orelse break :no_switch_on_err;1048 const capture_token = payload_token orelse break :no_switch_on_err;
1052 switch (node_tags[node_datas[node].rhs]) {1049 const full_switch = tree.fullSwitch(node_datas[node].rhs) orelse break :no_switch_on_err;
1053 .@"switch", .switch_comma => {},1050 if (full_switch.label_token != null) break :no_switch_on_err;
1054 else => break :no_switch_on_err,1051 if (node_tags[full_switch.ast.condition] != .identifier) break :no_switch_on_err;
1055 }1052 if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(main_tokens[full_switch.ast.condition]))) break :no_switch_on_err;
1056 const switch_operand = node_datas[node_datas[node].rhs].lhs;
1057 if (node_tags[switch_operand] != .identifier) break :no_switch_on_err;
1058 if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(main_tokens[switch_operand]))) break :no_switch_on_err;
1059 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch");1053 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch");
1060 }1054 }
1061 switch (ri.rl) {1055 switch (ri.rl) {
...@@ -2160,11 +2154,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -2160,11 +2154,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
2160 if (break_label != 0) {2154 if (break_label != 0) {
2161 if (block_gz.label) |*label| {2155 if (block_gz.label) |*label| {
2162 if (try astgen.tokenIdentEql(label.token, break_label)) {2156 if (try astgen.tokenIdentEql(label.token, break_label)) {
2163 const maybe_switch_tag = astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)];
2164 switch (maybe_switch_tag) {
2165 .switch_block, .switch_block_ref => return astgen.failNode(node, "cannot break from switch", .{}),
2166 else => {},
2167 }
2168 label.used = true;2157 label.used = true;
2169 break :blk label.block_inst;2158 break :blk label.block_inst;
2170 }2159 }
...@@ -2278,6 +2267,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)...@@ -2278,6 +2267,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
2278 }2267 }
22792268
2280 label.used = true;2269 label.used = true;
2270 label.used_for_continue = true;
2281 break :blk;2271 break :blk;
2282 }2272 }
2283 }2273 }
...@@ -7760,7 +7750,7 @@ fn switchExpr(...@@ -7760,7 +7750,7 @@ fn switchExpr(
7760 const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);7750 const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
7761 const item_ri: ResultInfo = .{ .rl = .none };7751 const item_ri: ResultInfo = .{ .rl = .none };
77627752
7763 // If this switch is labeled, it will have `continue`s targeting it, and thus we need the operand type7753 // If this switch is labeled, it may have `continue`s targeting it, and thus we need the operand type
7764 // to provide a result type.7754 // to provide a result type.
7765 const raw_operand_ty_ref = if (switch_full.label_token != null) t: {7755 const raw_operand_ty_ref = if (switch_full.label_token != null) t: {
7766 break :t try parent_gz.addUnNode(.typeof, raw_operand, operand_node);7756 break :t try parent_gz.addUnNode(.typeof, raw_operand, operand_node);
...@@ -7790,7 +7780,9 @@ fn switchExpr(...@@ -7790,7 +7780,9 @@ fn switchExpr(
7790 const switch_block = try parent_gz.makeBlockInst(switch_tag, node);7780 const switch_block = try parent_gz.makeBlockInst(switch_tag, node);
77917781
7792 if (switch_full.label_token) |label_token| {7782 if (switch_full.label_token) |label_token| {
7783 block_scope.break_block = switch_block.toOptional();
7793 block_scope.continue_block = switch_block.toOptional();7784 block_scope.continue_block = switch_block.toOptional();
7785 // `break_result_info` already set above
7794 block_scope.continue_result_info = .{7786 block_scope.continue_result_info = .{
7795 .rl = if (any_payload_is_ref)7787 .rl = if (any_payload_is_ref)
7796 .{ .ref_coerced_ty = raw_operand_ty_ref }7788 .{ .ref_coerced_ty = raw_operand_ty_ref }
...@@ -8024,7 +8016,7 @@ fn switchExpr(...@@ -8024,7 +8016,7 @@ fn switchExpr(
8024 .has_under = special_prong == .under,8016 .has_under = special_prong == .under,
8025 .any_has_tag_capture = any_has_tag_capture,8017 .any_has_tag_capture = any_has_tag_capture,
8026 .any_non_inline_capture = any_non_inline_capture,8018 .any_non_inline_capture = any_non_inline_capture,
8027 .has_continue = switch_full.label_token != null,8019 .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue,
8028 .scalar_cases_len = @intCast(scalar_cases_len),8020 .scalar_cases_len = @intCast(scalar_cases_len),
8029 },8021 },
8030 });8022 });
...@@ -11963,6 +11955,7 @@ const GenZir = struct {...@@ -11963,6 +11955,7 @@ const GenZir = struct {
11963 token: Ast.TokenIndex,11955 token: Ast.TokenIndex,
11964 block_inst: Zir.Inst.Index,11956 block_inst: Zir.Inst.Index,
11965 used: bool = false,11957 used: bool = false,
11958 used_for_continue: bool = false,
11966 };11959 };
1196711960
11968 /// Assumes nothing stacked on `gz`.11961 /// Assumes nothing stacked on `gz`.
test/behavior/switch.zig+24
...@@ -961,3 +961,27 @@ test "block error return trace index is reset between prongs" {...@@ -961,3 +961,27 @@ test "block error return trace index is reset between prongs" {
961 };961 };
962 try result;962 try result;
963}963}
964
965test "labeled switch with break" {
966 var six: u32 = undefined;
967 six = 6;
968
969 const val = s: switch (six) {
970 0...4 => break :s false,
971 5 => break :s false,
972 6...7 => break :s true,
973 else => break :s false,
974 };
975
976 try expect(val);
977
978 // Make sure the switch is implicitly comptime!
979 const comptime_val = s: switch (@as(u32, 6)) {
980 0...4 => break :s false,
981 5 => break :s false,
982 6...7 => break :s true,
983 else => break :s false,
984 };
985
986 comptime assert(comptime_val);
987}