authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-10-03 23:43:09-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-10-03 23:43:09-04:00
logff534d22676b8a934acf1931f91d70c554a4bdca
treed04b360f7831f6428d13a85d9d9c0d7c04fc1079
parent9d5462dcb5b4b4601bdf2e628b9d80fb74000cb2
parent17eea918aee98ca29c3762a7ecd568d2f14f66ef
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12979 from Vexu/inline-switch

Implement inline switch cases

20 files changed, 964 insertions(+), 222 deletions(-)

doc/langref.html.in+128
......@@ -4255,6 +4255,134 @@ test "enum literals with switch" {
42554255}
42564256 {#code_end#}
42574257 {#header_close#}
4258
4259 {#header_open|Inline switch#}
4260 <p>
4261 Switch prongs can be marked as {#syntax#}inline{#endsyntax#} to generate
4262 the prong's body for each possible value it could have:
4263 </p>
4264 {#code_begin|test|test_inline_switch#}
4265const std = @import("std");
4266const expect = std.testing.expect;
4267const expectError = std.testing.expectError;
4268
4269fn isFieldOptional(comptime T: type, field_index: usize) !bool {
4270 const fields = @typeInfo(T).Struct.fields;
4271 return switch (field_index) {
4272 // This prong is analyzed `fields.len - 1` times with `idx` being an
4273 // unique comptime known value each time.
4274 inline 0...fields.len - 1 => |idx| @typeInfo(fields[idx].field_type) == .Optional,
4275 else => return error.IndexOutOfBounds,
4276 };
4277}
4278
4279const Struct1 = struct { a: u32, b: ?u32 };
4280
4281test "using @typeInfo with runtime values" {
4282 var index: usize = 0;
4283 try expect(!try isFieldOptional(Struct1, index));
4284 index += 1;
4285 try expect(try isFieldOptional(Struct1, index));
4286 index += 1;
4287 try expectError(error.IndexOutOfBounds, isFieldOptional(Struct1, index));
4288}
4289
4290// Calls to `isFieldOptional` on `Struct1` get unrolled to an equivalent
4291// of this function:
4292fn isFieldOptionalUnrolled(field_index: usize) !bool {
4293 return switch (field_index) {
4294 0 => false,
4295 1 => true,
4296 else => return error.IndexOutOfBounds,
4297 };
4298}
4299 {#code_end#}
4300 <p>
4301 {#syntax#}inline else{#endsyntax#} prongs can be used as a type safe
4302 alternative to {#syntax#}inline for{#endsyntax#} loops:
4303 </p>
4304 {#code_begin|test|test_inline_else#}
4305const std = @import("std");
4306const expect = std.testing.expect;
4307
4308const SliceTypeA = extern struct {
4309 len: usize,
4310 ptr: [*]u32,
4311};
4312const SliceTypeB = extern struct {
4313 ptr: [*]SliceTypeA,
4314 len: usize,
4315};
4316const AnySlice = union(enum) {
4317 a: SliceTypeA,
4318 b: SliceTypeB,
4319 c: []const u8,
4320 d: []AnySlice,
4321};
4322
4323fn withFor(any: AnySlice) usize {
4324 const Tag = @typeInfo(AnySlice).Union.tag_type.?;
4325 inline for (@typeInfo(Tag).Enum.fields) |field| {
4326 // With `inline for` the function gets generated as
4327 // a series of `if` statements relying on the optimizer
4328 // to convert it to a switch.
4329 if (field.value == @enumToInt(any)) {
4330 return @field(any, field.name).len;
4331 }
4332 }
4333 // When using `inline for` the compiler doesn't know that every
4334 // possible case has been handled requiring an explicit `unreachable`.
4335 unreachable;
4336}
4337
4338fn withSwitch(any: AnySlice) usize {
4339 return switch (any) {
4340 // With `inline else` the function is explicitly generated
4341 // as the desired switch and the compiler can check that
4342 // every possible case is handled.
4343 inline else => |slice| slice.len,
4344 };
4345}
4346
4347test "inline for and inline else similarity" {
4348 var any = AnySlice{ .c = "hello" };
4349 try expect(withFor(any) == 5);
4350 try expect(withSwitch(any) == 5);
4351}
4352 {#code_end#}
4353 <p>
4354 When using an inline prong switching on an union an additional
4355 capture can be used to obtain the union's enum tag value.
4356 </p>
4357 {#code_begin|test|test_inline_switch_union_tag#}
4358const std = @import("std");
4359const expect = std.testing.expect;
4360
4361const U = union(enum) {
4362 a: u32,
4363 b: f32,
4364};
4365
4366fn getNum(u: U) u32 {
4367 switch (u) {
4368 // Here `num` is a runtime known value that is either
4369 // `u.a` or `u.b` and `tag` is `u`'s comptime known tag value.
4370 inline else => |num, tag| {
4371 if (tag == .b) {
4372 return @floatToInt(u32, num);
4373 }
4374 return num;
4375 }
4376 }
4377}
4378
4379test "test" {
4380 var u = U{ .b = 42 };
4381 try expect(getNum(u) == 42);
4382}
4383 {#code_end#}
4384 {#see_also|inline while|inline for#}
4385 {#header_close#}
42584386 {#header_close#}
42594387
42604388 {#header_open|while#}
lib/std/zig/Ast.zig+28-3
......@@ -643,11 +643,23 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
643643 n = datas[n].lhs;
644644 }
645645 },
646 .switch_case_inline_one => {
647 if (datas[n].lhs == 0) {
648 return main_tokens[n] - 2 - end_offset; // else token
649 } else {
650 return firstToken(tree, datas[n].lhs) - 1;
651 }
652 },
646653 .switch_case => {
647654 const extra = tree.extraData(datas[n].lhs, Node.SubRange);
648655 assert(extra.end - extra.start > 0);
649656 n = tree.extra_data[extra.start];
650657 },
658 .switch_case_inline => {
659 const extra = tree.extraData(datas[n].lhs, Node.SubRange);
660 assert(extra.end - extra.start > 0);
661 return firstToken(tree, tree.extra_data[extra.start]) - 1;
662 },
651663
652664 .asm_output, .asm_input => {
653665 assert(token_tags[main_tokens[n] - 1] == .l_bracket);
......@@ -763,7 +775,9 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
763775 .ptr_type_bit_range,
764776 .array_type,
765777 .switch_case_one,
778 .switch_case_inline_one,
766779 .switch_case,
780 .switch_case_inline,
767781 .switch_range,
768782 => n = datas[n].rhs,
769783
......@@ -1755,7 +1769,7 @@ pub fn switchCaseOne(tree: Ast, node: Node.Index) full.SwitchCase {
17551769 .values = if (data.lhs == 0) values[0..0] else values[0..1],
17561770 .arrow_token = tree.nodes.items(.main_token)[node],
17571771 .target_expr = data.rhs,
1758 });
1772 }, node);
17591773}
17601774
17611775pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
......@@ -1765,7 +1779,7 @@ pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
17651779 .values = tree.extra_data[extra.start..extra.end],
17661780 .arrow_token = tree.nodes.items(.main_token)[node],
17671781 .target_expr = data.rhs,
1768 });
1782 }, node);
17691783}
17701784
17711785pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm {
......@@ -2038,15 +2052,21 @@ fn fullContainerDecl(tree: Ast, info: full.ContainerDecl.Components) full.Contai
20382052 return result;
20392053}
20402054
2041fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components) full.SwitchCase {
2055fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components, node: Node.Index) full.SwitchCase {
20422056 const token_tags = tree.tokens.items(.tag);
2057 const node_tags = tree.nodes.items(.tag);
20432058 var result: full.SwitchCase = .{
20442059 .ast = info,
20452060 .payload_token = null,
2061 .inline_token = null,
20462062 };
20472063 if (token_tags[info.arrow_token + 1] == .pipe) {
20482064 result.payload_token = info.arrow_token + 2;
20492065 }
2066 switch (node_tags[node]) {
2067 .switch_case_inline, .switch_case_inline_one => result.inline_token = firstToken(tree, node),
2068 else => {},
2069 }
20502070 return result;
20512071}
20522072
......@@ -2454,6 +2474,7 @@ pub const full = struct {
24542474 };
24552475
24562476 pub const SwitchCase = struct {
2477 inline_token: ?TokenIndex,
24572478 /// Points to the first token after the `|`. Will either be an identifier or
24582479 /// a `*` (with an identifier immediately after it).
24592480 payload_token: ?TokenIndex,
......@@ -2847,9 +2868,13 @@ pub const Node = struct {
28472868 /// `lhs => rhs`. If lhs is omitted it means `else`.
28482869 /// main_token is the `=>`
28492870 switch_case_one,
2871 /// Same ast `switch_case_one` but the case is inline
2872 switch_case_inline_one,
28502873 /// `a, b, c => rhs`. `SubRange[lhs]`.
28512874 /// main_token is the `=>`
28522875 switch_case,
2876 /// Same ast `switch_case` but the case is inline
2877 switch_case_inline,
28532878 /// `lhs...rhs`.
28542879 switch_range,
28552880 /// `while (lhs) rhs`.
lib/std/zig/parse.zig+11-6
......@@ -3100,7 +3100,7 @@ const Parser = struct {
31003100 return identifier;
31013101 }
31023102
3103 /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
3103 /// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
31043104 /// SwitchCase
31053105 /// <- SwitchItem (COMMA SwitchItem)* COMMA?
31063106 /// / KEYWORD_else
......@@ -3108,6 +3108,8 @@ const Parser = struct {
31083108 const scratch_top = p.scratch.items.len;
31093109 defer p.scratch.shrinkRetainingCapacity(scratch_top);
31103110
3111 const is_inline = p.eatToken(.keyword_inline) != null;
3112
31113113 if (p.eatToken(.keyword_else) == null) {
31123114 while (true) {
31133115 const item = try p.parseSwitchItem();
......@@ -3115,15 +3117,18 @@ const Parser = struct {
31153117 try p.scratch.append(p.gpa, item);
31163118 if (p.eatToken(.comma) == null) break;
31173119 }
3118 if (scratch_top == p.scratch.items.len) return null_node;
3120 if (scratch_top == p.scratch.items.len) {
3121 if (is_inline) p.tok_i -= 1;
3122 return null_node;
3123 }
31193124 }
31203125 const arrow_token = try p.expectToken(.equal_angle_bracket_right);
3121 _ = try p.parsePtrPayload();
3126 _ = try p.parsePtrIndexPayload();
31223127
31233128 const items = p.scratch.items[scratch_top..];
31243129 switch (items.len) {
31253130 0 => return p.addNode(.{
3126 .tag = .switch_case_one,
3131 .tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
31273132 .main_token = arrow_token,
31283133 .data = .{
31293134 .lhs = 0,
......@@ -3131,7 +3136,7 @@ const Parser = struct {
31313136 },
31323137 }),
31333138 1 => return p.addNode(.{
3134 .tag = .switch_case_one,
3139 .tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
31353140 .main_token = arrow_token,
31363141 .data = .{
31373142 .lhs = items[0],
......@@ -3139,7 +3144,7 @@ const Parser = struct {
31393144 },
31403145 }),
31413146 else => return p.addNode(.{
3142 .tag = .switch_case,
3147 .tag = if (is_inline) .switch_case_inline else .switch_case,
31433148 .main_token = arrow_token,
31443149 .data = .{
31453150 .lhs = try p.addExtra(try p.listToSpan(items)),
lib/std/zig/parser_test.zig+2
......@@ -3276,6 +3276,8 @@ test "zig fmt: switch" {
32763276 \\ switch (u) {
32773277 \\ Union.Int => |int| {},
32783278 \\ Union.Float => |*float| unreachable,
3279 \\ 1 => |a, b| unreachable,
3280 \\ 2 => |*a, b| unreachable,
32793281 \\ }
32803282 \\}
32813283 \\
lib/std/zig/render.zig+15-6
......@@ -685,8 +685,8 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
685685 return renderToken(ais, tree, tree.lastToken(node), space); // rbrace
686686 },
687687
688 .switch_case_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
689 .switch_case => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
688 .switch_case_one, .switch_case_inline_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
689 .switch_case, .switch_case_inline => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
690690
691691 .while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space),
692692 .while_cont => return renderWhile(gpa, ais, tree, tree.whileCont(node), space),
......@@ -1509,6 +1509,11 @@ fn renderSwitchCase(
15091509 break :blk hasComment(tree, tree.firstToken(switch_case.ast.values[0]), switch_case.ast.arrow_token);
15101510 };
15111511
1512 // render inline keyword
1513 if (switch_case.inline_token) |some| {
1514 try renderToken(ais, tree, some, .space);
1515 }
1516
15121517 // Render everything before the arrow
15131518 if (switch_case.ast.values.len == 0) {
15141519 try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword
......@@ -1536,13 +1541,17 @@ fn renderSwitchCase(
15361541
15371542 if (switch_case.payload_token) |payload_token| {
15381543 try renderToken(ais, tree, payload_token - 1, .none); // pipe
1544 const ident = payload_token + @boolToInt(token_tags[payload_token] == .asterisk);
15391545 if (token_tags[payload_token] == .asterisk) {
15401546 try renderToken(ais, tree, payload_token, .none); // asterisk
1541 try renderToken(ais, tree, payload_token + 1, .none); // identifier
1542 try renderToken(ais, tree, payload_token + 2, pre_target_space); // pipe
1547 }
1548 try renderToken(ais, tree, ident, .none); // identifier
1549 if (token_tags[ident + 1] == .comma) {
1550 try renderToken(ais, tree, ident + 1, .space); // ,
1551 try renderToken(ais, tree, ident + 2, .none); // identifier
1552 try renderToken(ais, tree, ident + 3, pre_target_space); // pipe
15431553 } else {
1544 try renderToken(ais, tree, payload_token, .none); // identifier
1545 try renderToken(ais, tree, payload_token + 1, pre_target_space); // pipe
1554 try renderToken(ais, tree, ident + 1, pre_target_space); // pipe
15461555 }
15471556 }
15481557
src/AstGen.zig+118-52
......@@ -386,7 +386,9 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
386386 .simple_var_decl => unreachable,
387387 .aligned_var_decl => unreachable,
388388 .switch_case => unreachable,
389 .switch_case_inline => unreachable,
389390 .switch_case_one => unreachable,
391 .switch_case_inline_one => unreachable,
390392 .container_field_init => unreachable,
391393 .container_field_align => unreachable,
392394 .container_field => unreachable,
......@@ -600,7 +602,9 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
600602 .@"errdefer" => unreachable, // Handled in `blockExpr`.
601603
602604 .switch_case => unreachable, // Handled in `switchExpr`.
605 .switch_case_inline => unreachable, // Handled in `switchExpr`.
603606 .switch_case_one => unreachable, // Handled in `switchExpr`.
607 .switch_case_inline_one => unreachable, // Handled in `switchExpr`.
604608 .switch_range => unreachable, // Handled in `switchExpr`.
605609
606610 .asm_output => unreachable, // Handled in `asmExpr`.
......@@ -2369,6 +2373,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
23692373 .switch_capture_ref,
23702374 .switch_capture_multi,
23712375 .switch_capture_multi_ref,
2376 .switch_capture_tag,
23722377 .struct_init_empty,
23732378 .struct_init,
23742379 .struct_init_ref,
......@@ -6213,14 +6218,15 @@ fn switchExpr(
62136218 var any_payload_is_ref = false;
62146219 var scalar_cases_len: u32 = 0;
62156220 var multi_cases_len: u32 = 0;
6221 var inline_cases_len: u32 = 0;
62166222 var special_prong: Zir.SpecialProng = .none;
62176223 var special_node: Ast.Node.Index = 0;
62186224 var else_src: ?Ast.TokenIndex = null;
62196225 var underscore_src: ?Ast.TokenIndex = null;
62206226 for (case_nodes) |case_node| {
62216227 const case = switch (node_tags[case_node]) {
6222 .switch_case_one => tree.switchCaseOne(case_node),
6223 .switch_case => tree.switchCase(case_node),
6228 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
6229 .switch_case, .switch_case_inline => tree.switchCase(case_node),
62246230 else => unreachable,
62256231 };
62266232 if (case.payload_token) |payload_token| {
......@@ -6304,6 +6310,9 @@ fn switchExpr(
63046310 },
63056311 );
63066312 }
6313 if (case.inline_token != null) {
6314 return astgen.failTok(case_src, "cannot inline '_' prong", .{});
6315 }
63076316 special_node = case_node;
63086317 special_prong = .under;
63096318 underscore_src = case_src;
......@@ -6315,6 +6324,9 @@ fn switchExpr(
63156324 } else {
63166325 multi_cases_len += 1;
63176326 }
6327 if (case.inline_token != null) {
6328 inline_cases_len += 1;
6329 }
63186330 }
63196331
63206332 const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
......@@ -6354,8 +6366,8 @@ fn switchExpr(
63546366 var scalar_case_index: u32 = 0;
63556367 for (case_nodes) |case_node| {
63566368 const case = switch (node_tags[case_node]) {
6357 .switch_case_one => tree.switchCaseOne(case_node),
6358 .switch_case => tree.switchCase(case_node),
6369 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
6370 .switch_case, .switch_case_inline => tree.switchCase(case_node),
63596371 else => unreachable,
63606372 };
63616373
......@@ -6364,8 +6376,12 @@ fn switchExpr(
63646376
63656377 var dbg_var_name: ?u32 = null;
63666378 var dbg_var_inst: Zir.Inst.Ref = undefined;
6379 var dbg_var_tag_name: ?u32 = null;
6380 var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
63676381 var capture_inst: Zir.Inst.Index = 0;
6382 var tag_inst: Zir.Inst.Index = 0;
63686383 var capture_val_scope: Scope.LocalVal = undefined;
6384 var tag_scope: Scope.LocalVal = undefined;
63696385 const sub_scope = blk: {
63706386 const payload_token = case.payload_token orelse break :blk &case_scope.base;
63716387 const ident = if (token_tags[payload_token] == .asterisk)
......@@ -6373,59 +6389,96 @@ fn switchExpr(
63736389 else
63746390 payload_token;
63756391 const is_ptr = ident != payload_token;
6376 if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
6392 const ident_slice = tree.tokenSlice(ident);
6393 var payload_sub_scope: *Scope = undefined;
6394 if (mem.eql(u8, ident_slice, "_")) {
63776395 if (is_ptr) {
63786396 return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
63796397 }
6380 break :blk &case_scope.base;
6381 }
6382 if (case_node == special_node) {
6383 const capture_tag: Zir.Inst.Tag = if (is_ptr)
6384 .switch_capture_ref
6385 else
6386 .switch_capture;
6387 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
6388 try astgen.instructions.append(gpa, .{
6389 .tag = capture_tag,
6390 .data = .{
6391 .switch_capture = .{
6392 .switch_inst = switch_block,
6393 // Max int communicates that this is the else/underscore prong.
6394 .prong_index = std.math.maxInt(u32),
6395 },
6396 },
6397 });
6398 payload_sub_scope = &case_scope.base;
63986399 } else {
6399 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
6400 const is_ptr_bits: u2 = @boolToInt(is_ptr);
6401 const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
6402 0b00 => .switch_capture,
6403 0b01 => .switch_capture_ref,
6404 0b10 => .switch_capture_multi,
6405 0b11 => .switch_capture_multi_ref,
6400 if (case_node == special_node) {
6401 const capture_tag: Zir.Inst.Tag = if (is_ptr)
6402 .switch_capture_ref
6403 else
6404 .switch_capture;
6405 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
6406 try astgen.instructions.append(gpa, .{
6407 .tag = capture_tag,
6408 .data = .{
6409 .switch_capture = .{
6410 .switch_inst = switch_block,
6411 // Max int communicates that this is the else/underscore prong.
6412 .prong_index = std.math.maxInt(u32),
6413 },
6414 },
6415 });
6416 } else {
6417 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
6418 const is_ptr_bits: u2 = @boolToInt(is_ptr);
6419 const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
6420 0b00 => .switch_capture,
6421 0b01 => .switch_capture_ref,
6422 0b10 => .switch_capture_multi,
6423 0b11 => .switch_capture_multi_ref,
6424 };
6425 const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
6426 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
6427 try astgen.instructions.append(gpa, .{
6428 .tag = capture_tag,
6429 .data = .{ .switch_capture = .{
6430 .switch_inst = switch_block,
6431 .prong_index = capture_index,
6432 } },
6433 });
6434 }
6435 const capture_name = try astgen.identAsString(ident);
6436 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice);
6437 capture_val_scope = .{
6438 .parent = &case_scope.base,
6439 .gen_zir = &case_scope,
6440 .name = capture_name,
6441 .inst = indexToRef(capture_inst),
6442 .token_src = payload_token,
6443 .id_cat = .@"capture",
64066444 };
6407 const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
6408 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
6409 try astgen.instructions.append(gpa, .{
6410 .tag = capture_tag,
6411 .data = .{ .switch_capture = .{
6412 .switch_inst = switch_block,
6413 .prong_index = capture_index,
6414 } },
6415 });
6445 dbg_var_name = capture_name;
6446 dbg_var_inst = indexToRef(capture_inst);
6447 payload_sub_scope = &capture_val_scope.base;
6448 }
6449
6450 const tag_token = if (token_tags[ident + 1] == .comma)
6451 ident + 2
6452 else
6453 break :blk payload_sub_scope;
6454 const tag_slice = tree.tokenSlice(tag_token);
6455 if (mem.eql(u8, tag_slice, "_")) {
6456 return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
6457 } else if (case.inline_token == null) {
6458 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
64166459 }
6417 const capture_name = try astgen.identAsString(ident);
6418 capture_val_scope = .{
6419 .parent = &case_scope.base,
6460 const tag_name = try astgen.identAsString(tag_token);
6461 try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice);
6462 tag_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
6463 try astgen.instructions.append(gpa, .{
6464 .tag = .switch_capture_tag,
6465 .data = .{ .un_tok = .{
6466 .operand = cond,
6467 .src_tok = case_scope.tokenIndexToRelative(tag_token),
6468 } },
6469 });
6470
6471 tag_scope = .{
6472 .parent = payload_sub_scope,
64206473 .gen_zir = &case_scope,
6421 .name = capture_name,
6422 .inst = indexToRef(capture_inst),
6423 .token_src = payload_token,
6424 .id_cat = .@"capture",
6474 .name = tag_name,
6475 .inst = indexToRef(tag_inst),
6476 .token_src = tag_token,
6477 .id_cat = .@"switch tag capture",
64256478 };
6426 dbg_var_name = capture_name;
6427 dbg_var_inst = indexToRef(capture_inst);
6428 break :blk &capture_val_scope.base;
6479 dbg_var_tag_name = tag_name;
6480 dbg_var_tag_inst = indexToRef(tag_inst);
6481 break :blk &tag_scope.base;
64296482 };
64306483
64316484 const header_index = @intCast(u32, payloads.items.len);
......@@ -6480,10 +6533,14 @@ fn switchExpr(
64806533 defer case_scope.unstack();
64816534
64826535 if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst);
6536 if (tag_inst != 0) try case_scope.instructions.append(gpa, tag_inst);
64836537 try case_scope.addDbgBlockBegin();
64846538 if (dbg_var_name) |some| {
64856539 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
64866540 }
6541 if (dbg_var_tag_name) |some| {
6542 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_tag_inst);
6543 }
64876544 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
64886545 try checkUsed(parent_gz, &case_scope.base, sub_scope);
64896546 try case_scope.addDbgBlockEnd();
......@@ -6495,7 +6552,8 @@ fn switchExpr(
64956552 const case_slice = case_scope.instructionsSlice();
64966553 const body_len = astgen.countBodyLenAfterFixups(case_slice);
64976554 try payloads.ensureUnusedCapacity(gpa, body_len);
6498 payloads.items[body_len_index] = body_len;
6555 const inline_bit = @as(u32, @boolToInt(case.inline_token != null)) << 31;
6556 payloads.items[body_len_index] = body_len | inline_bit;
64996557 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
65006558 }
65016559 }
......@@ -6509,7 +6567,6 @@ fn switchExpr(
65096567 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
65106568 .operand = cond,
65116569 .bits = Zir.Inst.SwitchBlock.Bits{
6512 .is_ref = any_payload_is_ref,
65136570 .has_multi_cases = multi_cases_len != 0,
65146571 .has_else = special_prong == .@"else",
65156572 .has_under = special_prong == .under,
......@@ -6543,7 +6600,7 @@ fn switchExpr(
65436600 end_index += 3 + items_len + 2 * ranges_len;
65446601 }
65456602
6546 const body_len = payloads.items[body_len_index];
6603 const body_len = @truncate(u31, payloads.items[body_len_index]);
65476604 end_index += body_len;
65486605
65496606 switch (strat.tag) {
......@@ -8433,7 +8490,9 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_
84338490 .@"usingnamespace",
84348491 .test_decl,
84358492 .switch_case,
8493 .switch_case_inline,
84368494 .switch_case_one,
8495 .switch_case_inline_one,
84378496 .container_field_init,
84388497 .container_field_align,
84398498 .container_field,
......@@ -8665,7 +8724,9 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
86658724 .@"usingnamespace",
86668725 .test_decl,
86678726 .switch_case,
8727 .switch_case_inline,
86688728 .switch_case_one,
8729 .switch_case_inline_one,
86698730 .container_field_init,
86708731 .container_field_align,
86718732 .container_field,
......@@ -8876,7 +8937,9 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
88768937 .@"usingnamespace",
88778938 .test_decl,
88788939 .switch_case,
8940 .switch_case_inline,
88798941 .switch_case_one,
8942 .switch_case_inline_one,
88808943 .container_field_init,
88818944 .container_field_align,
88828945 .container_field,
......@@ -9118,7 +9181,9 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
91189181 .@"usingnamespace",
91199182 .test_decl,
91209183 .switch_case,
9184 .switch_case_inline,
91219185 .switch_case_one,
9186 .switch_case_inline_one,
91229187 .container_field_init,
91239188 .container_field_align,
91249189 .container_field,
......@@ -10051,6 +10116,7 @@ const Scope = struct {
1005110116 @"local constant",
1005210117 @"local variable",
1005310118 @"loop index capture",
10119 @"switch tag capture",
1005410120 @"capture",
1005510121 };
1005610122
src/Module.zig+8-8
......@@ -2445,8 +2445,8 @@ pub const SrcLoc = struct {
24452445 const case_nodes = tree.extra_data[extra.start..extra.end];
24462446 for (case_nodes) |case_node| {
24472447 const case = switch (node_tags[case_node]) {
2448 .switch_case_one => tree.switchCaseOne(case_node),
2449 .switch_case => tree.switchCase(case_node),
2448 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2449 .switch_case, .switch_case_inline => tree.switchCase(case_node),
24502450 else => unreachable,
24512451 };
24522452 const is_special = (case.ast.values.len == 0) or
......@@ -2469,8 +2469,8 @@ pub const SrcLoc = struct {
24692469 const case_nodes = tree.extra_data[extra.start..extra.end];
24702470 for (case_nodes) |case_node| {
24712471 const case = switch (node_tags[case_node]) {
2472 .switch_case_one => tree.switchCaseOne(case_node),
2473 .switch_case => tree.switchCase(case_node),
2472 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2473 .switch_case, .switch_case_inline => tree.switchCase(case_node),
24742474 else => unreachable,
24752475 };
24762476 const is_special = (case.ast.values.len == 0) or
......@@ -2491,8 +2491,8 @@ pub const SrcLoc = struct {
24912491 const case_node = src_loc.declRelativeToNodeIndex(node_off);
24922492 const node_tags = tree.nodes.items(.tag);
24932493 const case = switch (node_tags[case_node]) {
2494 .switch_case_one => tree.switchCaseOne(case_node),
2495 .switch_case => tree.switchCase(case_node),
2494 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2495 .switch_case, .switch_case_inline => tree.switchCase(case_node),
24962496 else => unreachable,
24972497 };
24982498 const start_tok = case.payload_token.?;
......@@ -5940,8 +5940,8 @@ pub const SwitchProngSrc = union(enum) {
59405940 var scalar_i: u32 = 0;
59415941 for (case_nodes) |case_node| {
59425942 const case = switch (node_tags[case_node]) {
5943 .switch_case_one => tree.switchCaseOne(case_node),
5944 .switch_case => tree.switchCase(case_node),
5943 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
5944 .switch_case, .switch_case_inline => tree.switchCase(case_node),
59455945 else => unreachable,
59465946 };
59475947 if (case.ast.values.len == 0)
src/Sema.zig+407-124
......@@ -162,6 +162,9 @@ pub const Block = struct {
162162 /// type of `err` in `else => |err|`
163163 switch_else_err_ty: ?Type = null,
164164
165 /// Value for switch_capture in an inline case
166 inline_case_capture: Air.Inst.Ref = .none,
167
165168 const Param = struct {
166169 /// `noreturn` means `anytype`.
167170 ty: Type,
......@@ -603,6 +606,21 @@ fn resolveBody(
603606 return try sema.resolveInst(break_data.operand);
604607}
605608
609fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !void {
610 _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
611 error.ComptimeBreak => {
612 const zir_datas = sema.code.instructions.items(.data);
613 const break_data = zir_datas[sema.comptime_break_inst].@"break";
614 try sema.addRuntimeBreak(block, .{
615 .block_inst = break_data.block_inst,
616 .operand = break_data.operand,
617 .inst = sema.comptime_break_inst,
618 });
619 },
620 else => |e| return e,
621 };
622}
623
606624pub fn analyzeBody(
607625 sema: *Sema,
608626 block: *Block,
......@@ -796,6 +814,7 @@ fn analyzeBodyInner(
796814 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
797815 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
798816 .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
817 .switch_capture_tag => try sema.zirSwitchCaptureTag(block, inst),
799818 .type_info => try sema.zirTypeInfo(block, inst),
800819 .size_of => try sema.zirSizeOf(block, inst),
801820 .bit_size_of => try sema.zirBitSizeOf(block, inst),
......@@ -9030,13 +9049,38 @@ fn zirSwitchCapture(
90309049 const switch_info = zir_datas[capture_info.switch_inst].pl_node;
90319050 const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);
90329051 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };
9033 const operand_is_ref = switch_extra.data.bits.is_ref;
90349052 const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;
9035 const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node;
9053 const cond_info = zir_datas[cond_inst].un_node;
9054 const cond_tag = sema.code.instructions.items(.tag)[cond_inst];
9055 const operand_is_ref = cond_tag == .switch_cond_ref;
90369056 const operand_ptr = try sema.resolveInst(cond_info.operand);
90379057 const operand_ptr_ty = sema.typeOf(operand_ptr);
90389058 const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
90399059
9060 if (block.inline_case_capture != .none) {
9061 const item_val = sema.resolveConstValue(block, .unneeded, block.inline_case_capture, undefined) catch unreachable;
9062 if (operand_ty.zigTypeTag() == .Union) {
9063 const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, sema.mod).?);
9064 const union_obj = operand_ty.cast(Type.Payload.Union).?.data;
9065 const field_ty = union_obj.fields.values()[field_index].ty;
9066 if (is_ref) {
9067 const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{
9068 .pointee_type = field_ty,
9069 .mutable = operand_ptr_ty.ptrIsMutable(),
9070 .@"volatile" = operand_ptr_ty.isVolatilePtr(),
9071 .@"addrspace" = operand_ptr_ty.ptrAddressSpace(),
9072 });
9073 return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty);
9074 } else {
9075 return block.addStructFieldVal(operand_ptr, field_index, field_ty);
9076 }
9077 } else if (is_ref) {
9078 return sema.addConstantMaybeRef(block, operand_src, operand_ty, item_val, true);
9079 } else {
9080 return block.inline_case_capture;
9081 }
9082 }
9083
90409084 const operand = if (operand_is_ref)
90419085 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
90429086 else
......@@ -9045,7 +9089,6 @@ fn zirSwitchCapture(
90459089 if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {
90469090 // It is the else/`_` prong.
90479091 if (is_ref) {
9048 assert(operand_is_ref);
90499092 return operand_ptr;
90509093 }
90519094
......@@ -9105,8 +9148,6 @@ fn zirSwitchCapture(
91059148 }
91069149
91079150 if (is_ref) {
9108 assert(operand_is_ref);
9109
91109151 const field_ty_ptr = try Type.ptr(sema.arena, sema.mod, .{
91119152 .pointee_type = first_field.ty,
91129153 .@"addrspace" = .generic,
......@@ -9167,7 +9208,6 @@ fn zirSwitchCapture(
91679208 // In this case the capture value is just the passed-through value of the
91689209 // switch condition.
91699210 if (is_ref) {
9170 assert(operand_is_ref);
91719211 return operand_ptr;
91729212 } else {
91739213 return operand;
......@@ -9176,6 +9216,33 @@ fn zirSwitchCapture(
91769216 }
91779217}
91789218
9219fn zirSwitchCaptureTag(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9220 const zir_datas = sema.code.instructions.items(.data);
9221 const inst_data = zir_datas[inst].un_tok;
9222 const src = inst_data.src();
9223
9224 const switch_tag = sema.code.instructions.items(.tag)[Zir.refToIndex(inst_data.operand).?];
9225 const is_ref = switch_tag == .switch_cond_ref;
9226 const cond_data = zir_datas[Zir.refToIndex(inst_data.operand).?].un_node;
9227 const operand_ptr = try sema.resolveInst(cond_data.operand);
9228 const operand_ptr_ty = sema.typeOf(operand_ptr);
9229 const operand_ty = if (is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
9230
9231 if (operand_ty.zigTypeTag() != .Union) {
9232 const msg = msg: {
9233 const msg = try sema.errMsg(block, src, "cannot capture tag of non-union type '{}'", .{
9234 operand_ty.fmt(sema.mod),
9235 });
9236 errdefer msg.destroy(sema.gpa);
9237 try sema.addDeclaredHereNote(msg, operand_ty);
9238 break :msg msg;
9239 };
9240 return sema.failWithOwnedErrorMsg(msg);
9241 }
9242
9243 return block.inline_case_capture;
9244}
9245
91799246fn zirSwitchCond(
91809247 sema: *Sema,
91819248 block: *Block,
......@@ -9273,14 +9340,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
92739340 } else 0;
92749341
92759342 const special_prong = extra.data.bits.specialProng();
9276 const special: struct { body: []const Zir.Inst.Index, end: usize } = switch (special_prong) {
9277 .none => .{ .body = &.{}, .end = header_extra_index },
9343 const special: struct { body: []const Zir.Inst.Index, end: usize, is_inline: bool } = switch (special_prong) {
9344 .none => .{ .body = &.{}, .end = header_extra_index, .is_inline = false },
92789345 .under, .@"else" => blk: {
9279 const body_len = sema.code.extra[header_extra_index];
9346 const body_len = @truncate(u31, sema.code.extra[header_extra_index]);
92809347 const extra_body_start = header_extra_index + 1;
92819348 break :blk .{
92829349 .body = sema.code.extra[extra_body_start..][0..body_len],
92839350 .end = extra_body_start + body_len,
9351 .is_inline = sema.code.extra[header_extra_index] >> 31 != 0,
92849352 };
92859353 },
92869354 };
......@@ -9292,8 +9360,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
92929360 break :blk sema.typeOf(raw_operand);
92939361 };
92949362 const union_originally = maybe_union_ty.zigTypeTag() == .Union;
9295 var seen_union_fields: []?Module.SwitchProngSrc = &.{};
9296 defer gpa.free(seen_union_fields);
9363
9364 // Duplicate checking variables later also used for `inline else`.
9365 var seen_enum_fields: []?Module.SwitchProngSrc = &.{};
9366 var seen_errors = SwitchErrorSet.init(gpa);
9367 var range_set = RangeSet.init(gpa, sema.mod);
9368 var true_count: u8 = 0;
9369 var false_count: u8 = 0;
9370
9371 defer {
9372 range_set.deinit();
9373 gpa.free(seen_enum_fields);
9374 seen_errors.deinit();
9375 }
92979376
92989377 var empty_enum = false;
92999378
......@@ -9330,15 +9409,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
93309409 switch (operand_ty.zigTypeTag()) {
93319410 .Union => unreachable, // handled in zirSwitchCond
93329411 .Enum => {
9333 var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());
9334 empty_enum = seen_fields.len == 0 and !operand_ty.isNonexhaustiveEnum();
9335 defer if (!union_originally) gpa.free(seen_fields);
9336 if (union_originally) seen_union_fields = seen_fields;
9337 mem.set(?Module.SwitchProngSrc, seen_fields, null);
9338
9339 // This is used for non-exhaustive enum values that do not correspond to any tags.
9340 var range_set = RangeSet.init(gpa, sema.mod);
9341 defer range_set.deinit();
9412 seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());
9413 empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum();
9414 mem.set(?Module.SwitchProngSrc, seen_enum_fields, null);
9415 // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
93429416
93439417 var extra_index: usize = special.end;
93449418 {
......@@ -9346,13 +9420,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
93469420 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
93479421 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
93489422 extra_index += 1;
9349 const body_len = sema.code.extra[extra_index];
9423 const body_len = @truncate(u31, sema.code.extra[extra_index]);
93509424 extra_index += 1;
93519425 extra_index += body_len;
93529426
93539427 try sema.validateSwitchItemEnum(
93549428 block,
9355 seen_fields,
9429 seen_enum_fields,
93569430 &range_set,
93579431 item_ref,
93589432 src_node_offset,
......@@ -9367,7 +9441,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
93679441 extra_index += 1;
93689442 const ranges_len = sema.code.extra[extra_index];
93699443 extra_index += 1;
9370 const body_len = sema.code.extra[extra_index];
9444 const body_len = @truncate(u31, sema.code.extra[extra_index]);
93719445 extra_index += 1;
93729446 const items = sema.code.refSlice(extra_index, items_len);
93739447 extra_index += items_len + body_len;
......@@ -9375,7 +9449,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
93759449 for (items) |item_ref, item_i| {
93769450 try sema.validateSwitchItemEnum(
93779451 block,
9378 seen_fields,
9452 seen_enum_fields,
93799453 &range_set,
93809454 item_ref,
93819455 src_node_offset,
......@@ -9386,7 +9460,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
93869460 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
93879461 }
93889462 }
9389 const all_tags_handled = for (seen_fields) |seen_src| {
9463 const all_tags_handled = for (seen_enum_fields) |seen_src| {
93909464 if (seen_src == null) break false;
93919465 } else true;
93929466
......@@ -9406,7 +9480,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
94069480 .{},
94079481 );
94089482 errdefer msg.destroy(sema.gpa);
9409 for (seen_fields) |seen_src, i| {
9483 for (seen_enum_fields) |seen_src, i| {
94109484 if (seen_src != null) continue;
94119485
94129486 const field_name = operand_ty.enumFieldName(i);
......@@ -9437,16 +9511,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
94379511 }
94389512 },
94399513 .ErrorSet => {
9440 var seen_errors = SwitchErrorSet.init(gpa);
9441 defer seen_errors.deinit();
9442
94439514 var extra_index: usize = special.end;
94449515 {
94459516 var scalar_i: u32 = 0;
94469517 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
94479518 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
94489519 extra_index += 1;
9449 const body_len = sema.code.extra[extra_index];
9520 const body_len = @truncate(u31, sema.code.extra[extra_index]);
94509521 extra_index += 1;
94519522 extra_index += body_len;
94529523
......@@ -9466,7 +9537,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
94669537 extra_index += 1;
94679538 const ranges_len = sema.code.extra[extra_index];
94689539 extra_index += 1;
9469 const body_len = sema.code.extra[extra_index];
9540 const body_len = @truncate(u31, sema.code.extra[extra_index]);
94709541 extra_index += 1;
94719542 const items = sema.code.refSlice(extra_index, items_len);
94729543 extra_index += items_len + body_len;
......@@ -9579,16 +9650,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
95799650 }
95809651 },
95819652 .Int, .ComptimeInt => {
9582 var range_set = RangeSet.init(gpa, sema.mod);
9583 defer range_set.deinit();
9584
95859653 var extra_index: usize = special.end;
95869654 {
95879655 var scalar_i: u32 = 0;
95889656 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
95899657 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
95909658 extra_index += 1;
9591 const body_len = sema.code.extra[extra_index];
9659 const body_len = @truncate(u31, sema.code.extra[extra_index]);
95929660 extra_index += 1;
95939661 extra_index += body_len;
95949662
......@@ -9609,7 +9677,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
96099677 extra_index += 1;
96109678 const ranges_len = sema.code.extra[extra_index];
96119679 extra_index += 1;
9612 const body_len = sema.code.extra[extra_index];
9680 const body_len = @truncate(u31, sema.code.extra[extra_index]);
96139681 extra_index += 1;
96149682 const items = sema.code.refSlice(extra_index, items_len);
96159683 extra_index += items_len;
......@@ -9677,16 +9745,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
96779745 }
96789746 },
96799747 .Bool => {
9680 var true_count: u8 = 0;
9681 var false_count: u8 = 0;
9682
96839748 var extra_index: usize = special.end;
96849749 {
96859750 var scalar_i: u32 = 0;
96869751 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
96879752 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
96889753 extra_index += 1;
9689 const body_len = sema.code.extra[extra_index];
9754 const body_len = @truncate(u31, sema.code.extra[extra_index]);
96909755 extra_index += 1;
96919756 extra_index += body_len;
96929757
......@@ -9707,7 +9772,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
97079772 extra_index += 1;
97089773 const ranges_len = sema.code.extra[extra_index];
97099774 extra_index += 1;
9710 const body_len = sema.code.extra[extra_index];
9775 const body_len = @truncate(u31, sema.code.extra[extra_index]);
97119776 extra_index += 1;
97129777 const items = sema.code.refSlice(extra_index, items_len);
97139778 extra_index += items_len + body_len;
......@@ -9771,7 +9836,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
97719836 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
97729837 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
97739838 extra_index += 1;
9774 const body_len = sema.code.extra[extra_index];
9839 const body_len = @truncate(u31, sema.code.extra[extra_index]);
97759840 extra_index += 1;
97769841 extra_index += body_len;
97779842
......@@ -9791,7 +9856,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
97919856 extra_index += 1;
97929857 const ranges_len = sema.code.extra[extra_index];
97939858 extra_index += 1;
9794 const body_len = sema.code.extra[extra_index];
9859 const body_len = @truncate(u31, sema.code.extra[extra_index]);
97959860 extra_index += 1;
97969861 const items = sema.code.refSlice(extra_index, items_len);
97979862 extra_index += items_len + body_len;
......@@ -9871,7 +9936,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
98719936 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
98729937 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
98739938 extra_index += 1;
9874 const body_len = sema.code.extra[extra_index];
9939 const body_len = @truncate(u31, sema.code.extra[extra_index]);
98759940 extra_index += 1;
98769941 const body = sema.code.extra[extra_index..][0..body_len];
98779942 extra_index += body_len;
......@@ -9892,7 +9957,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
98929957 extra_index += 1;
98939958 const ranges_len = sema.code.extra[extra_index];
98949959 extra_index += 1;
9895 const body_len = sema.code.extra[extra_index];
9960 const body_len = @truncate(u31, sema.code.extra[extra_index]);
98969961 extra_index += 1;
98979962 const items = sema.code.refSlice(extra_index, items_len);
98989963 extra_index += items_len;
......@@ -9933,7 +9998,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
99339998 return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
99349999 }
993510000
9936 if (scalar_cases_len + multi_cases_len == 0) {
10001 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
993710002 if (empty_enum) {
993810003 return Air.Inst.Ref.void_value;
993910004 }
......@@ -9965,7 +10030,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
996510030 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
996610031 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
996710032 extra_index += 1;
9968 const body_len = sema.code.extra[extra_index];
10033 const body_len = @truncate(u31, sema.code.extra[extra_index]);
10034 const is_inline = sema.code.extra[extra_index] >> 31 != 0;
996910035 extra_index += 1;
997010036 const body = sema.code.extra[extra_index..][0..body_len];
997110037 extra_index += body_len;
......@@ -9975,8 +10041,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
997510041
997610042 case_block.instructions.shrinkRetainingCapacity(0);
997710043 case_block.wip_capture_scope = wip_captures.scope;
10044 case_block.inline_case_capture = .none;
997810045
997910046 const item = try sema.resolveInst(item_ref);
10047 if (is_inline) case_block.inline_case_capture = item;
998010048 // `item` is already guaranteed to be constant known.
998110049
998210050 const analyze_body = if (union_originally) blk: {
......@@ -9988,18 +10056,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
998810056 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
998910057 // nothing to do here
999010058 } else if (analyze_body) {
9991 _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
9992 error.ComptimeBreak => {
9993 const zir_datas = sema.code.instructions.items(.data);
9994 const break_data = zir_datas[sema.comptime_break_inst].@"break";
9995 try sema.addRuntimeBreak(&case_block, .{
9996 .block_inst = break_data.block_inst,
9997 .operand = break_data.operand,
9998 .inst = sema.comptime_break_inst,
9999 });
10000 },
10001 else => |e| return e,
10002 };
10059 try sema.analyzeBodyRuntimeBreak(&case_block, body);
1000310060 } else {
1000410061 _ = try case_block.addNoOp(.unreach);
1000510062 }
......@@ -10021,19 +10078,115 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1002110078 defer gpa.free(prev_then_body);
1002210079
1002310080 var cases_len = scalar_cases_len;
10024 var multi_i: usize = 0;
10081 var multi_i: u32 = 0;
1002510082 while (multi_i < multi_cases_len) : (multi_i += 1) {
1002610083 const items_len = sema.code.extra[extra_index];
1002710084 extra_index += 1;
1002810085 const ranges_len = sema.code.extra[extra_index];
1002910086 extra_index += 1;
10030 const body_len = sema.code.extra[extra_index];
10087 const body_len = @truncate(u31, sema.code.extra[extra_index]);
10088 const is_inline = sema.code.extra[extra_index] >> 31 != 0;
1003110089 extra_index += 1;
1003210090 const items = sema.code.refSlice(extra_index, items_len);
1003310091 extra_index += items_len;
1003410092
1003510093 case_block.instructions.shrinkRetainingCapacity(0);
1003610094 case_block.wip_capture_scope = child_block.wip_capture_scope;
10095 case_block.inline_case_capture = .none;
10096
10097 // Generate all possible cases as scalar prongs.
10098 if (is_inline) {
10099 const body_start = extra_index + 2 * ranges_len;
10100 const body = sema.code.extra[body_start..][0..body_len];
10101 var emit_bb = false;
10102
10103 var range_i: u32 = 0;
10104 while (range_i < ranges_len) : (range_i += 1) {
10105 const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
10106 extra_index += 1;
10107 const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
10108 extra_index += 1;
10109
10110 const item_first_ref = try sema.resolveInst(first_ref);
10111 var item = sema.resolveConstValue(block, .unneeded, item_first_ref, undefined) catch unreachable;
10112 const item_last_ref = try sema.resolveInst(last_ref);
10113 const item_last = sema.resolveConstValue(block, .unneeded, item_last_ref, undefined) catch unreachable;
10114
10115 while (item.compare(.lte, item_last, operand_ty, sema.mod)) : ({
10116 // Previous validation has resolved any possible lazy values.
10117 item = try sema.intAddScalar(block, .unneeded, item, Value.one);
10118 }) {
10119 cases_len += 1;
10120
10121 const item_ref = try sema.addConstant(operand_ty, item);
10122 case_block.inline_case_capture = item_ref;
10123
10124 case_block.instructions.shrinkRetainingCapacity(0);
10125 case_block.wip_capture_scope = child_block.wip_capture_scope;
10126
10127 if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
10128 error.NeededSourceLocation => {
10129 const case_src = Module.SwitchProngSrc{ .range = .{ .prong = multi_i, .item = range_i } };
10130 const decl = sema.mod.declPtr(case_block.src_decl);
10131 try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none));
10132 return error.AnalysisFail;
10133 },
10134 else => return err,
10135 };
10136 emit_bb = true;
10137
10138 try sema.analyzeBodyRuntimeBreak(&case_block, body);
10139
10140 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10141 cases_extra.appendAssumeCapacity(1); // items_len
10142 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10143 cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
10144 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10145 }
10146 }
10147
10148 for (items) |item_ref, item_i| {
10149 cases_len += 1;
10150
10151 const item = try sema.resolveInst(item_ref);
10152 case_block.inline_case_capture = item;
10153
10154 case_block.instructions.shrinkRetainingCapacity(0);
10155 case_block.wip_capture_scope = child_block.wip_capture_scope;
10156
10157 const analyze_body = if (union_originally) blk: {
10158 const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
10159 const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
10160 break :blk field_ty.zigTypeTag() != .NoReturn;
10161 } else true;
10162
10163 if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
10164 error.NeededSourceLocation => {
10165 const case_src = Module.SwitchProngSrc{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } };
10166 const decl = sema.mod.declPtr(case_block.src_decl);
10167 try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none));
10168 return error.AnalysisFail;
10169 },
10170 else => return err,
10171 };
10172 emit_bb = true;
10173
10174 if (analyze_body) {
10175 try sema.analyzeBodyRuntimeBreak(&case_block, body);
10176 } else {
10177 _ = try case_block.addNoOp(.unreach);
10178 }
10179
10180 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10181 cases_extra.appendAssumeCapacity(1); // items_len
10182 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10183 cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
10184 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10185 }
10186
10187 extra_index += body_len;
10188 continue;
10189 }
1003710190
1003810191 var any_ok: Air.Inst.Ref = .none;
1003910192
......@@ -10058,18 +10211,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1005810211 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
1005910212 // nothing to do here
1006010213 } else if (analyze_body) {
10061 _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
10062 error.ComptimeBreak => {
10063 const zir_datas = sema.code.instructions.items(.data);
10064 const break_data = zir_datas[sema.comptime_break_inst].@"break";
10065 try sema.addRuntimeBreak(&case_block, .{
10066 .block_inst = break_data.block_inst,
10067 .operand = break_data.operand,
10068 .inst = sema.comptime_break_inst,
10069 });
10070 },
10071 else => |e| return e,
10072 };
10214 try sema.analyzeBodyRuntimeBreak(&case_block, body);
1007310215 } else {
1007410216 _ = try case_block.addNoOp(.unreach);
1007510217 }
......@@ -10150,18 +10292,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1015010292 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
1015110293 // nothing to do here
1015210294 } else {
10153 _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
10154 error.ComptimeBreak => {
10155 const zir_datas = sema.code.instructions.items(.data);
10156 const break_data = zir_datas[sema.comptime_break_inst].@"break";
10157 try sema.addRuntimeBreak(&case_block, .{
10158 .block_inst = break_data.block_inst,
10159 .operand = break_data.operand,
10160 .inst = sema.comptime_break_inst,
10161 });
10162 },
10163 else => |e| return e,
10164 };
10295 try sema.analyzeBodyRuntimeBreak(&case_block, body);
1016510296 }
1016610297
1016710298 try wip_captures.finalize();
......@@ -10192,14 +10323,150 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1019210323
1019310324 var final_else_body: []const Air.Inst.Index = &.{};
1019410325 if (special.body.len != 0 or !is_first or case_block.wantSafety()) {
10326 var emit_bb = false;
10327 if (special.is_inline) switch (operand_ty.zigTypeTag()) {
10328 .Enum => {
10329 if (operand_ty.isNonexhaustiveEnum() and !union_originally) {
10330 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
10331 operand_ty.fmt(sema.mod),
10332 });
10333 }
10334 for (seen_enum_fields) |f, i| {
10335 if (f != null) continue;
10336 cases_len += 1;
10337
10338 const item_val = try Value.Tag.enum_field_index.create(sema.arena, @intCast(u32, i));
10339 const item_ref = try sema.addConstant(operand_ty, item_val);
10340 case_block.inline_case_capture = item_ref;
10341
10342 case_block.instructions.shrinkRetainingCapacity(0);
10343 case_block.wip_capture_scope = child_block.wip_capture_scope;
10344
10345 const analyze_body = if (union_originally) blk: {
10346 const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
10347 break :blk field_ty.zigTypeTag() != .NoReturn;
10348 } else true;
10349
10350 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
10351 emit_bb = true;
10352
10353 if (analyze_body) {
10354 try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
10355 } else {
10356 _ = try case_block.addNoOp(.unreach);
10357 }
10358
10359 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10360 cases_extra.appendAssumeCapacity(1); // items_len
10361 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10362 cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
10363 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10364 }
10365 },
10366 .ErrorSet => {
10367 if (operand_ty.isAnyError()) {
10368 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
10369 operand_ty.fmt(sema.mod),
10370 });
10371 }
10372 for (operand_ty.errorSetNames()) |error_name| {
10373 if (seen_errors.contains(error_name)) continue;
10374 cases_len += 1;
10375
10376 const item_val = try Value.Tag.@"error".create(sema.arena, .{ .name = error_name });
10377 const item_ref = try sema.addConstant(operand_ty, item_val);
10378 case_block.inline_case_capture = item_ref;
10379
10380 case_block.instructions.shrinkRetainingCapacity(0);
10381 case_block.wip_capture_scope = child_block.wip_capture_scope;
10382
10383 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
10384 emit_bb = true;
10385
10386 try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
10387
10388 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10389 cases_extra.appendAssumeCapacity(1); // items_len
10390 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10391 cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
10392 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10393 }
10394 },
10395 .Int => {
10396 var it = try RangeSetUnhandledIterator.init(sema, block, special_prong_src, operand_ty, range_set);
10397 while (try it.next()) |cur| {
10398 cases_len += 1;
10399
10400 const item_ref = try sema.addConstant(operand_ty, cur);
10401 case_block.inline_case_capture = item_ref;
10402
10403 case_block.instructions.shrinkRetainingCapacity(0);
10404 case_block.wip_capture_scope = child_block.wip_capture_scope;
10405
10406 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
10407 emit_bb = true;
10408
10409 try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
10410
10411 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10412 cases_extra.appendAssumeCapacity(1); // items_len
10413 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10414 cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
10415 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10416 }
10417 },
10418 .Bool => {
10419 if (true_count == 0) {
10420 cases_len += 1;
10421 case_block.inline_case_capture = Air.Inst.Ref.bool_true;
10422
10423 case_block.instructions.shrinkRetainingCapacity(0);
10424 case_block.wip_capture_scope = child_block.wip_capture_scope;
10425
10426 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
10427 emit_bb = true;
10428
10429 try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
10430
10431 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10432 cases_extra.appendAssumeCapacity(1); // items_len
10433 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10434 cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
10435 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10436 }
10437 if (false_count == 0) {
10438 cases_len += 1;
10439 case_block.inline_case_capture = Air.Inst.Ref.bool_false;
10440
10441 case_block.instructions.shrinkRetainingCapacity(0);
10442 case_block.wip_capture_scope = child_block.wip_capture_scope;
10443
10444 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
10445 emit_bb = true;
10446
10447 try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
10448
10449 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10450 cases_extra.appendAssumeCapacity(1); // items_len
10451 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10452 cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
10453 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10454 }
10455 },
10456 else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
10457 operand_ty.fmt(sema.mod),
10458 }),
10459 };
10460
1019510461 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
1019610462 defer wip_captures.deinit();
1019710463
1019810464 case_block.instructions.shrinkRetainingCapacity(0);
1019910465 case_block.wip_capture_scope = wip_captures.scope;
10466 case_block.inline_case_capture = .none;
1020010467
10201 const analyze_body = if (union_originally)
10202 for (seen_union_fields) |seen_field, index| {
10468 const analyze_body = if (union_originally and !special.is_inline)
10469 for (seen_enum_fields) |seen_field, index| {
1020310470 if (seen_field != null) continue;
1020410471 const union_obj = maybe_union_ty.cast(Type.Payload.Union).?.data;
1020510472 const field_ty = union_obj.fields.values()[index].ty;
......@@ -10211,19 +10478,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1021110478 try sema.maybeErrorUnwrap(&case_block, special.body, operand))
1021210479 {
1021310480 // nothing to do here
10214 } else if (special.body.len != 0 and analyze_body) {
10215 _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) {
10216 error.ComptimeBreak => {
10217 const zir_datas = sema.code.instructions.items(.data);
10218 const break_data = zir_datas[sema.comptime_break_inst].@"break";
10219 try sema.addRuntimeBreak(&case_block, .{
10220 .block_inst = break_data.block_inst,
10221 .operand = break_data.operand,
10222 .inst = sema.comptime_break_inst,
10223 });
10224 },
10225 else => |e| return e,
10226 };
10481 } else if (special.body.len != 0 and analyze_body and !special.is_inline) {
10482 try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
1022710483 } else {
1022810484 // We still need a terminator in this block, but we have proven
1022910485 // that it is unreachable.
......@@ -10269,6 +10525,55 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1026910525 return sema.analyzeBlockBody(block, src, &child_block, merges);
1027010526}
1027110527
10528const RangeSetUnhandledIterator = struct {
10529 sema: *Sema,
10530 block: *Block,
10531 src: LazySrcLoc,
10532 ty: Type,
10533 cur: Value,
10534 max: Value,
10535 ranges: []const RangeSet.Range,
10536 range_i: usize = 0,
10537 first: bool = true,
10538
10539 fn init(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator {
10540 const target = sema.mod.getTarget();
10541 const min = try ty.minInt(sema.arena, target);
10542 const max = try ty.maxInt(sema.arena, target);
10543
10544 return RangeSetUnhandledIterator{
10545 .sema = sema,
10546 .block = block,
10547 .src = src,
10548 .ty = ty,
10549 .cur = min,
10550 .max = max,
10551 .ranges = range_set.ranges.items,
10552 };
10553 }
10554
10555 fn next(it: *RangeSetUnhandledIterator) !?Value {
10556 while (it.range_i < it.ranges.len) : (it.range_i += 1) {
10557 if (!it.first) {
10558 it.cur = try it.sema.intAdd(it.block, it.src, it.cur, Value.one, it.ty);
10559 }
10560 it.first = false;
10561 if (it.cur.compare(.lt, it.ranges[it.range_i].first, it.ty, it.sema.mod)) {
10562 return it.cur;
10563 }
10564 it.cur = it.ranges[it.range_i].last;
10565 }
10566 if (!it.first) {
10567 it.cur = try it.sema.intAdd(it.block, it.src, it.cur, Value.one, it.ty);
10568 }
10569 it.first = false;
10570 if (it.cur.compare(.lte, it.max, it.ty, it.sema.mod)) {
10571 return it.cur;
10572 }
10573 return null;
10574 }
10575};
10576
1027210577fn resolveSwitchItemVal(
1027310578 sema: *Sema,
1027410579 block: *Block,
......@@ -15351,18 +15656,7 @@ fn zirCondbr(
1535115656 sub_block.runtime_index.increment();
1535215657 defer sub_block.instructions.deinit(gpa);
1535315658
15354 _ = sema.analyzeBodyInner(&sub_block, then_body) catch |err| switch (err) {
15355 error.ComptimeBreak => {
15356 const zir_datas = sema.code.instructions.items(.data);
15357 const break_data = zir_datas[sema.comptime_break_inst].@"break";
15358 try sema.addRuntimeBreak(&sub_block, .{
15359 .block_inst = break_data.block_inst,
15360 .operand = break_data.operand,
15361 .inst = sema.comptime_break_inst,
15362 });
15363 },
15364 else => |e| return e,
15365 };
15659 try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
1536615660 const true_instructions = sub_block.instructions.toOwnedSlice(gpa);
1536715661 defer gpa.free(true_instructions);
1536815662
......@@ -15381,18 +15675,7 @@ fn zirCondbr(
1538115675 if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?)) {
1538215676 // nothing to do
1538315677 } else {
15384 _ = sema.analyzeBodyInner(&sub_block, else_body) catch |err| switch (err) {
15385 error.ComptimeBreak => {
15386 const zir_datas = sema.code.instructions.items(.data);
15387 const break_data = zir_datas[sema.comptime_break_inst].@"break";
15388 try sema.addRuntimeBreak(&sub_block, .{
15389 .block_inst = break_data.block_inst,
15390 .operand = break_data.operand,
15391 .inst = sema.comptime_break_inst,
15392 });
15393 },
15394 else => |e| return e,
15395 };
15678 try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
1539615679 }
1539715680 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
1539815681 true_instructions.len + sub_block.instructions.items.len);
src/Zir.zig+15-12
......@@ -683,6 +683,9 @@ pub const Inst = struct {
683683 /// Result is a pointer to the value.
684684 /// Uses the `switch_capture` field.
685685 switch_capture_multi_ref,
686 /// Produces the capture value for an inline switch prong tag capture.
687 /// Uses the `un_tok` field.
688 switch_capture_tag,
686689 /// Given a
687690 /// *A returns *A
688691 /// *E!A returns *A
......@@ -1128,6 +1131,7 @@ pub const Inst = struct {
11281131 .switch_capture_ref,
11291132 .switch_capture_multi,
11301133 .switch_capture_multi_ref,
1134 .switch_capture_tag,
11311135 .switch_block,
11321136 .switch_cond,
11331137 .switch_cond_ref,
......@@ -1422,6 +1426,7 @@ pub const Inst = struct {
14221426 .switch_capture_ref,
14231427 .switch_capture_multi,
14241428 .switch_capture_multi_ref,
1429 .switch_capture_tag,
14251430 .switch_block,
14261431 .switch_cond,
14271432 .switch_cond_ref,
......@@ -1681,6 +1686,7 @@ pub const Inst = struct {
16811686 .switch_capture_ref = .switch_capture,
16821687 .switch_capture_multi = .switch_capture,
16831688 .switch_capture_multi_ref = .switch_capture,
1689 .switch_capture_tag = .un_tok,
16841690 .array_base_ptr = .un_node,
16851691 .field_base_ptr = .un_node,
16861692 .validate_array_init_ty = .pl_node,
......@@ -2952,12 +2958,9 @@ pub const Inst = struct {
29522958 has_else: bool,
29532959 /// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
29542960 has_under: bool,
2955 /// If true, the `operand` is a pointer to the value being switched on.
2956 /// TODO this flag is redundant with the tag of operand and can be removed.
2957 is_ref: bool,
29582961 scalar_cases_len: ScalarCasesLen,
29592962
2960 pub const ScalarCasesLen = u28;
2963 pub const ScalarCasesLen = u29;
29612964
29622965 pub fn specialProng(bits: Bits) SpecialProng {
29632966 const has_else: u2 = @boolToInt(bits.has_else);
......@@ -2993,7 +2996,7 @@ pub const Inst = struct {
29932996 }
29942997
29952998 if (self.bits.specialProng() != .none) {
2996 const body_len = zir.extra[extra_index];
2999 const body_len = @truncate(u31, zir.extra[extra_index]);
29973000 extra_index += 1;
29983001 const body = zir.extra[extra_index..][0..body_len];
29993002 extra_index += body.len;
......@@ -3003,7 +3006,7 @@ pub const Inst = struct {
30033006 while (true) : (scalar_i += 1) {
30043007 const item = @intToEnum(Ref, zir.extra[extra_index]);
30053008 extra_index += 1;
3006 const body_len = zir.extra[extra_index];
3009 const body_len = @truncate(u31, zir.extra[extra_index]);
30073010 extra_index += 1;
30083011 const body = zir.extra[extra_index..][0..body_len];
30093012 extra_index += body.len;
......@@ -3032,7 +3035,7 @@ pub const Inst = struct {
30323035 var extra_index: usize = extra_end + 1;
30333036
30343037 if (self.bits.specialProng() != .none) {
3035 const body_len = zir.extra[extra_index];
3038 const body_len = @truncate(u31, zir.extra[extra_index]);
30363039 extra_index += 1;
30373040 const body = zir.extra[extra_index..][0..body_len];
30383041 extra_index += body.len;
......@@ -3041,7 +3044,7 @@ pub const Inst = struct {
30413044 var scalar_i: usize = 0;
30423045 while (scalar_i < self.bits.scalar_cases_len) : (scalar_i += 1) {
30433046 extra_index += 1;
3044 const body_len = zir.extra[extra_index];
3047 const body_len = @truncate(u31, zir.extra[extra_index]);
30453048 extra_index += 1;
30463049 extra_index += body_len;
30473050 }
......@@ -3049,7 +3052,7 @@ pub const Inst = struct {
30493052 while (true) : (multi_i += 1) {
30503053 const items_len = zir.extra[extra_index];
30513054 extra_index += 2;
3052 const body_len = zir.extra[extra_index];
3055 const body_len = @truncate(u31, zir.extra[extra_index]);
30533056 extra_index += 1;
30543057 const items = zir.refSlice(extra_index, items_len);
30553058 extra_index += items_len;
......@@ -3861,7 +3864,7 @@ fn findDeclsSwitch(
38613864
38623865 const special_prong = extra.data.bits.specialProng();
38633866 if (special_prong != .none) {
3864 const body_len = zir.extra[extra_index];
3867 const body_len = @truncate(u31, zir.extra[extra_index]);
38653868 extra_index += 1;
38663869 const body = zir.extra[extra_index..][0..body_len];
38673870 extra_index += body.len;
......@@ -3874,7 +3877,7 @@ fn findDeclsSwitch(
38743877 var scalar_i: usize = 0;
38753878 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
38763879 extra_index += 1;
3877 const body_len = zir.extra[extra_index];
3880 const body_len = @truncate(u31, zir.extra[extra_index]);
38783881 extra_index += 1;
38793882 const body = zir.extra[extra_index..][0..body_len];
38803883 extra_index += body_len;
......@@ -3889,7 +3892,7 @@ fn findDeclsSwitch(
38893892 extra_index += 1;
38903893 const ranges_len = zir.extra[extra_index];
38913894 extra_index += 1;
3892 const body_len = zir.extra[extra_index];
3895 const body_len = @truncate(u31, zir.extra[extra_index]);
38933896 extra_index += 1;
38943897 const items = zir.refSlice(extra_index, items_len);
38953898 extra_index += items_len;
src/arch/x86_64/Emit.zig+1-1
......@@ -2159,7 +2159,7 @@ const RegisterOrMemory = union(enum) {
21592159 /// Returns size in bits.
21602160 fn size(reg_or_mem: RegisterOrMemory) u64 {
21612161 return switch (reg_or_mem) {
2162 .register => |reg| reg.size(),
2162 .register => |register| register.size(),
21632163 .memory => |memory| memory.size(),
21642164 };
21652165 }
src/print_zir.zig+10-5
......@@ -237,6 +237,7 @@ const Writer = struct {
237237 .ret_tok,
238238 .ensure_err_payload_void,
239239 .closure_capture,
240 .switch_capture_tag,
240241 => try self.writeUnTok(stream, inst),
241242
242243 .bool_br_and,
......@@ -1857,7 +1858,6 @@ const Writer = struct {
18571858 } else 0;
18581859
18591860 try self.writeInstRef(stream, extra.data.operand);
1860 try self.writeFlag(stream, ", ref", extra.data.bits.is_ref);
18611861
18621862 self.indent += 2;
18631863
......@@ -1869,14 +1869,15 @@ const Writer = struct {
18691869 else => break :else_prong,
18701870 };
18711871
1872 const body_len = self.code.extra[extra_index];
1872 const body_len = @truncate(u31, self.code.extra[extra_index]);
1873 const inline_text = if (self.code.extra[extra_index] >> 31 != 0) "inline " else "";
18731874 extra_index += 1;
18741875 const body = self.code.extra[extra_index..][0..body_len];
18751876 extra_index += body.len;
18761877
18771878 try stream.writeAll(",\n");
18781879 try stream.writeByteNTimes(' ', self.indent);
1879 try stream.print("{s} => ", .{prong_name});
1880 try stream.print("{s}{s} => ", .{ inline_text, prong_name });
18801881 try self.writeBracedBody(stream, body);
18811882 }
18821883
......@@ -1886,13 +1887,15 @@ const Writer = struct {
18861887 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
18871888 const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
18881889 extra_index += 1;
1889 const body_len = self.code.extra[extra_index];
1890 const body_len = @truncate(u31, self.code.extra[extra_index]);
1891 const is_inline = self.code.extra[extra_index] >> 31 != 0;
18901892 extra_index += 1;
18911893 const body = self.code.extra[extra_index..][0..body_len];
18921894 extra_index += body_len;
18931895
18941896 try stream.writeAll(",\n");
18951897 try stream.writeByteNTimes(' ', self.indent);
1898 if (is_inline) try stream.writeAll("inline ");
18961899 try self.writeInstRef(stream, item_ref);
18971900 try stream.writeAll(" => ");
18981901 try self.writeBracedBody(stream, body);
......@@ -1905,13 +1908,15 @@ const Writer = struct {
19051908 extra_index += 1;
19061909 const ranges_len = self.code.extra[extra_index];
19071910 extra_index += 1;
1908 const body_len = self.code.extra[extra_index];
1911 const body_len = @truncate(u31, self.code.extra[extra_index]);
1912 const is_inline = self.code.extra[extra_index] >> 31 != 0;
19091913 extra_index += 1;
19101914 const items = self.code.refSlice(extra_index, items_len);
19111915 extra_index += items_len;
19121916
19131917 try stream.writeAll(",\n");
19141918 try stream.writeByteNTimes(' ', self.indent);
1919 if (is_inline) try stream.writeAll("inline ");
19151920
19161921 for (items) |item_ref, item_i| {
19171922 if (item_i != 0) try stream.writeAll(", ");
src/stage1/all_types.hpp+1
......@@ -1039,6 +1039,7 @@ struct AstNodeSwitchProng {
10391039 AstNode *expr;
10401040 bool var_is_ptr;
10411041 bool any_items_are_range;
1042 bool is_inline;
10421043};
10431044
10441045struct AstNodeSwitchRange {
src/stage1/astgen.cpp+6
......@@ -6987,6 +6987,12 @@ static bool astgen_switch_prong_expr(Stage1AstGen *ag, Scope *scope, AstNode *sw
69876987 assert(switch_node->type == NodeTypeSwitchExpr);
69886988 assert(prong_node->type == NodeTypeSwitchProng);
69896989
6990 if (prong_node->data.switch_prong.is_inline) {
6991 exec_add_error_node(ag->codegen, ag->exec, prong_node,
6992 buf_sprintf("inline switch cases not supported by stage1"));
6993 return ag->codegen->invalid_inst_src;
6994 }
6995
69906996 AstNode *expr_node = prong_node->data.switch_prong.expr;
69916997 AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol;
69926998 Scope *child_scope;
src/stage1/parser.cpp+11-5
......@@ -2306,17 +2306,17 @@ static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc) {
23062306 return Optional<PtrIndexPayload>::some(res);
23072307}
23082308
2309// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
2309// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
23102310static AstNode *ast_parse_switch_prong(ParseContext *pc) {
23112311 AstNode *res = ast_parse_switch_case(pc);
23122312 if (res == nullptr)
23132313 return nullptr;
23142314
23152315 expect_token(pc, TokenIdFatArrow);
2316 Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc);
2316 Optional<PtrIndexPayload> opt_payload = ast_parse_ptr_index_payload(pc);
23172317 AstNode *expr = ast_expect(pc, ast_parse_assign_expr);
23182318
2319 PtrPayload payload;
2319 PtrIndexPayload payload;
23202320 assert(res->type == NodeTypeSwitchProng);
23212321 res->data.switch_prong.expr = expr;
23222322 if (opt_payload.unwrap(&payload)) {
......@@ -2331,9 +2331,11 @@ static AstNode *ast_parse_switch_prong(ParseContext *pc) {
23312331// <- SwitchItem (COMMA SwitchItem)* COMMA?
23322332// / KEYWORD_else
23332333static AstNode *ast_parse_switch_case(ParseContext *pc) {
2334 bool is_inline = eat_token_if(pc, TokenIdKeywordInline) != 0;
23342335 AstNode *first = ast_parse_switch_item(pc);
23352336 if (first != nullptr) {
23362337 AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeSwitchProng, first);
2338 res->data.switch_prong.is_inline = is_inline;
23372339 res->data.switch_prong.items.append(first);
23382340 res->data.switch_prong.any_items_are_range = first->type == NodeTypeSwitchRange;
23392341
......@@ -2350,9 +2352,13 @@ static AstNode *ast_parse_switch_case(ParseContext *pc) {
23502352 }
23512353
23522354 TokenIndex else_token = eat_token_if(pc, TokenIdKeywordElse);
2353 if (else_token != 0)
2354 return ast_create_node(pc, NodeTypeSwitchProng, else_token);
2355 if (else_token != 0) {
2356 AstNode *res = ast_create_node(pc, NodeTypeSwitchProng, else_token);
2357 res->data.switch_prong.is_inline = is_inline;
2358 return res;
2359 }
23552360
2361 if (is_inline) pc->current_token -= 1;
23562362 return nullptr;
23572363}
23582364
test/behavior.zig+1
......@@ -182,6 +182,7 @@ test {
182182 _ = @import("behavior/decltest.zig");
183183 _ = @import("behavior/packed_struct_explicit_backing_int.zig");
184184 _ = @import("behavior/empty_union.zig");
185 _ = @import("behavior/inline_switch.zig");
185186 }
186187
187188 if (builtin.os.tag != .wasi) {
test/behavior/inline_switch.zig created+131
......@@ -0,0 +1,131 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const builtin = @import("builtin");
4
5test "inline scalar prongs" {
6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7
8 var x: usize = 0;
9 switch (x) {
10 10 => |*item| try expect(@TypeOf(item) == *usize),
11 inline 11 => |*item| {
12 try expect(@TypeOf(item) == *const usize);
13 try expect(item.* == 11);
14 },
15 else => {},
16 }
17}
18
19test "inline prong ranges" {
20 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
21
22 var x: usize = 0;
23 switch (x) {
24 inline 0...20, 24 => |item| {
25 if (item > 25) @compileError("bad");
26 },
27 else => {},
28 }
29}
30
31const E = enum { a, b, c, d };
32test "inline switch enums" {
33 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
34
35 var x: E = .a;
36 switch (x) {
37 inline .a, .b => |aorb| if (aorb != .a and aorb != .b) @compileError("bad"),
38 inline .c, .d => |cord| if (cord != .c and cord != .d) @compileError("bad"),
39 }
40}
41
42const U = union(E) { a: void, b: u2, c: u3, d: u4 };
43test "inline switch unions" {
44 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
45 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
46 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
47
48 var x: U = .a;
49 switch (x) {
50 inline .a, .b => |aorb, tag| {
51 if (tag == .a) {
52 try expect(@TypeOf(aorb) == void);
53 } else {
54 try expect(tag == .b);
55 try expect(@TypeOf(aorb) == u2);
56 }
57 },
58 inline .c, .d => |cord, tag| {
59 if (tag == .c) {
60 try expect(@TypeOf(cord) == u3);
61 } else {
62 try expect(tag == .d);
63 try expect(@TypeOf(cord) == u4);
64 }
65 },
66 }
67}
68
69test "inline else bool" {
70 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
71
72 var a = true;
73 switch (a) {
74 true => {},
75 inline else => |val| if (val != false) @compileError("bad"),
76 }
77}
78
79test "inline else error" {
80 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
81
82 const Err = error{ a, b, c };
83 var a = Err.a;
84 switch (a) {
85 error.a => {},
86 inline else => |val| comptime if (val == error.a) @compileError("bad"),
87 }
88}
89
90test "inline else enum" {
91 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
92 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
93
94 const E2 = enum(u8) { a = 2, b = 3, c = 4, d = 5 };
95 var a: E2 = .a;
96 switch (a) {
97 .a, .b => {},
98 inline else => |val| comptime if (@enumToInt(val) < 4) @compileError("bad"),
99 }
100}
101
102test "inline else int with gaps" {
103 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
104
105 var a: u8 = 0;
106 switch (a) {
107 1...125, 128...254 => {},
108 inline else => |val| {
109 if (val != 0 and
110 val != 126 and
111 val != 127 and
112 val != 255)
113 @compileError("bad");
114 },
115 }
116}
117
118test "inline else int all values" {
119 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
120
121 var a: u2 = 0;
122 switch (a) {
123 inline else => |val| {
124 if (val != 0 and
125 val != 1 and
126 val != 2 and
127 val != 3)
128 @compileError("bad");
129 },
130 }
131}
test/cases/compile_errors/inline_underscore_prong.zig created+15
......@@ -0,0 +1,15 @@
1const E = enum(u8) { a, b, c, d, _ };
2pub export fn entry() void {
3 var x: E = .a;
4 switch (x) {
5 inline .a, .b => |aorb| @compileLog(aorb),
6 .c, .d => |cord| @compileLog(cord),
7 inline _ => {},
8 }
9}
10
11// error
12// backend=stage2
13// target=native
14//
15// :7:16: error: cannot inline '_' prong
test/cases/compile_errors/invalid_inline_else_type.zig created+27
......@@ -0,0 +1,27 @@
1pub export fn entry1() void {
2 var a: anyerror = undefined;
3 switch (a) {
4 inline else => {},
5 }
6}
7const E = enum(u8) { a, _ };
8pub export fn entry2() void {
9 var a: E = undefined;
10 switch (a) {
11 inline else => {},
12 }
13}
14pub export fn entry3() void {
15 var a: *u32 = undefined;
16 switch (a) {
17 inline else => {},
18 }
19}
20
21// error
22// backend=stage2
23// target=native
24//
25// :4:21: error: cannot enumerate values of type 'anyerror' for 'inline else'
26// :11:21: error: cannot enumerate values of type 'tmp.E' for 'inline else'
27// :17:21: error: cannot enumerate values of type '*u32' for 'inline else'
test/cases/compile_errors/invalid_tag_capture.zig created+15
......@@ -0,0 +1,15 @@
1const E = enum { a, b, c, d };
2pub export fn entry() void {
3 var x: E = .a;
4 switch (x) {
5 inline .a, .b => |aorb, d| @compileLog(aorb, d),
6 inline .c, .d => |*cord| @compileLog(cord),
7 }
8}
9
10// error
11// backend=stage2
12// target=native
13//
14// :5:33: error: cannot capture tag of non-union type 'tmp.E'
15// :1:11: note: enum declared here
test/cases/compile_errors/tag_capture_on_non_inline_prong.zig created+14
......@@ -0,0 +1,14 @@
1const E = enum { a, b, c, d };
2pub export fn entry() void {
3 var x: E = .a;
4 switch (x) {
5 .a, .b => |aorb, d| @compileLog(aorb, d),
6 inline .c, .d => |*cord| @compileLog(cord),
7 }
8}
9
10// error
11// backend=stage2
12// target=native
13//
14// :5:26: error: tag capture on non-inline prong