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" {...@@ -4255,6 +4255,134 @@ test "enum literals with switch" {
4255}4255}
4256 {#code_end#}4256 {#code_end#}
4257 {#header_close#}4257 {#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#}
4258 {#header_close#}4386 {#header_close#}
42594387
4260 {#header_open|while#}4388 {#header_open|while#}
lib/std/zig/Ast.zig+28-3
...@@ -643,11 +643,23 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -643,11 +643,23 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
643 n = datas[n].lhs;643 n = datas[n].lhs;
644 }644 }
645 },645 },
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 },
646 .switch_case => {653 .switch_case => {
647 const extra = tree.extraData(datas[n].lhs, Node.SubRange);654 const extra = tree.extraData(datas[n].lhs, Node.SubRange);
648 assert(extra.end - extra.start > 0);655 assert(extra.end - extra.start > 0);
649 n = tree.extra_data[extra.start];656 n = tree.extra_data[extra.start];
650 },657 },
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
652 .asm_output, .asm_input => {664 .asm_output, .asm_input => {
653 assert(token_tags[main_tokens[n] - 1] == .l_bracket);665 assert(token_tags[main_tokens[n] - 1] == .l_bracket);
...@@ -763,7 +775,9 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {...@@ -763,7 +775,9 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
763 .ptr_type_bit_range,775 .ptr_type_bit_range,
764 .array_type,776 .array_type,
765 .switch_case_one,777 .switch_case_one,
778 .switch_case_inline_one,
766 .switch_case,779 .switch_case,
780 .switch_case_inline,
767 .switch_range,781 .switch_range,
768 => n = datas[n].rhs,782 => n = datas[n].rhs,
769783
...@@ -1755,7 +1769,7 @@ pub fn switchCaseOne(tree: Ast, node: Node.Index) full.SwitchCase {...@@ -1755,7 +1769,7 @@ pub fn switchCaseOne(tree: Ast, node: Node.Index) full.SwitchCase {
1755 .values = if (data.lhs == 0) values[0..0] else values[0..1],1769 .values = if (data.lhs == 0) values[0..0] else values[0..1],
1756 .arrow_token = tree.nodes.items(.main_token)[node],1770 .arrow_token = tree.nodes.items(.main_token)[node],
1757 .target_expr = data.rhs,1771 .target_expr = data.rhs,
1758 });1772 }, node);
1759}1773}
17601774
1761pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {1775pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
...@@ -1765,7 +1779,7 @@ pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {...@@ -1765,7 +1779,7 @@ pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
1765 .values = tree.extra_data[extra.start..extra.end],1779 .values = tree.extra_data[extra.start..extra.end],
1766 .arrow_token = tree.nodes.items(.main_token)[node],1780 .arrow_token = tree.nodes.items(.main_token)[node],
1767 .target_expr = data.rhs,1781 .target_expr = data.rhs,
1768 });1782 }, node);
1769}1783}
17701784
1771pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm {1785pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm {
...@@ -2038,15 +2052,21 @@ fn fullContainerDecl(tree: Ast, info: full.ContainerDecl.Components) full.Contai...@@ -2038,15 +2052,21 @@ fn fullContainerDecl(tree: Ast, info: full.ContainerDecl.Components) full.Contai
2038 return result;2052 return result;
2039}2053}
20402054
2041fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components) full.SwitchCase {2055fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components, node: Node.Index) full.SwitchCase {
2042 const token_tags = tree.tokens.items(.tag);2056 const token_tags = tree.tokens.items(.tag);
2057 const node_tags = tree.nodes.items(.tag);
2043 var result: full.SwitchCase = .{2058 var result: full.SwitchCase = .{
2044 .ast = info,2059 .ast = info,
2045 .payload_token = null,2060 .payload_token = null,
2061 .inline_token = null,
2046 };2062 };
2047 if (token_tags[info.arrow_token + 1] == .pipe) {2063 if (token_tags[info.arrow_token + 1] == .pipe) {
2048 result.payload_token = info.arrow_token + 2;2064 result.payload_token = info.arrow_token + 2;
2049 }2065 }
2066 switch (node_tags[node]) {
2067 .switch_case_inline, .switch_case_inline_one => result.inline_token = firstToken(tree, node),
2068 else => {},
2069 }
2050 return result;2070 return result;
2051}2071}
20522072
...@@ -2454,6 +2474,7 @@ pub const full = struct {...@@ -2454,6 +2474,7 @@ pub const full = struct {
2454 };2474 };
24552475
2456 pub const SwitchCase = struct {2476 pub const SwitchCase = struct {
2477 inline_token: ?TokenIndex,
2457 /// Points to the first token after the `|`. Will either be an identifier or2478 /// Points to the first token after the `|`. Will either be an identifier or
2458 /// a `*` (with an identifier immediately after it).2479 /// a `*` (with an identifier immediately after it).
2459 payload_token: ?TokenIndex,2480 payload_token: ?TokenIndex,
...@@ -2847,9 +2868,13 @@ pub const Node = struct {...@@ -2847,9 +2868,13 @@ pub const Node = struct {
2847 /// `lhs => rhs`. If lhs is omitted it means `else`.2868 /// `lhs => rhs`. If lhs is omitted it means `else`.
2848 /// main_token is the `=>`2869 /// main_token is the `=>`
2849 switch_case_one,2870 switch_case_one,
2871 /// Same ast `switch_case_one` but the case is inline
2872 switch_case_inline_one,
2850 /// `a, b, c => rhs`. `SubRange[lhs]`.2873 /// `a, b, c => rhs`. `SubRange[lhs]`.
2851 /// main_token is the `=>`2874 /// main_token is the `=>`
2852 switch_case,2875 switch_case,
2876 /// Same ast `switch_case` but the case is inline
2877 switch_case_inline,
2853 /// `lhs...rhs`.2878 /// `lhs...rhs`.
2854 switch_range,2879 switch_range,
2855 /// `while (lhs) rhs`.2880 /// `while (lhs) rhs`.
lib/std/zig/parse.zig+11-6
...@@ -3100,7 +3100,7 @@ const Parser = struct {...@@ -3100,7 +3100,7 @@ const Parser = struct {
3100 return identifier;3100 return identifier;
3101 }3101 }
31023102
3103 /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr3103 /// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
3104 /// SwitchCase3104 /// SwitchCase
3105 /// <- SwitchItem (COMMA SwitchItem)* COMMA?3105 /// <- SwitchItem (COMMA SwitchItem)* COMMA?
3106 /// / KEYWORD_else3106 /// / KEYWORD_else
...@@ -3108,6 +3108,8 @@ const Parser = struct {...@@ -3108,6 +3108,8 @@ const Parser = struct {
3108 const scratch_top = p.scratch.items.len;3108 const scratch_top = p.scratch.items.len;
3109 defer p.scratch.shrinkRetainingCapacity(scratch_top);3109 defer p.scratch.shrinkRetainingCapacity(scratch_top);
31103110
3111 const is_inline = p.eatToken(.keyword_inline) != null;
3112
3111 if (p.eatToken(.keyword_else) == null) {3113 if (p.eatToken(.keyword_else) == null) {
3112 while (true) {3114 while (true) {
3113 const item = try p.parseSwitchItem();3115 const item = try p.parseSwitchItem();
...@@ -3115,15 +3117,18 @@ const Parser = struct {...@@ -3115,15 +3117,18 @@ const Parser = struct {
3115 try p.scratch.append(p.gpa, item);3117 try p.scratch.append(p.gpa, item);
3116 if (p.eatToken(.comma) == null) break;3118 if (p.eatToken(.comma) == null) break;
3117 }3119 }
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 }
3119 }3124 }
3120 const arrow_token = try p.expectToken(.equal_angle_bracket_right);3125 const arrow_token = try p.expectToken(.equal_angle_bracket_right);
3121 _ = try p.parsePtrPayload();3126 _ = try p.parsePtrIndexPayload();
31223127
3123 const items = p.scratch.items[scratch_top..];3128 const items = p.scratch.items[scratch_top..];
3124 switch (items.len) {3129 switch (items.len) {
3125 0 => return p.addNode(.{3130 0 => return p.addNode(.{
3126 .tag = .switch_case_one,3131 .tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
3127 .main_token = arrow_token,3132 .main_token = arrow_token,
3128 .data = .{3133 .data = .{
3129 .lhs = 0,3134 .lhs = 0,
...@@ -3131,7 +3136,7 @@ const Parser = struct {...@@ -3131,7 +3136,7 @@ const Parser = struct {
3131 },3136 },
3132 }),3137 }),
3133 1 => return p.addNode(.{3138 1 => return p.addNode(.{
3134 .tag = .switch_case_one,3139 .tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
3135 .main_token = arrow_token,3140 .main_token = arrow_token,
3136 .data = .{3141 .data = .{
3137 .lhs = items[0],3142 .lhs = items[0],
...@@ -3139,7 +3144,7 @@ const Parser = struct {...@@ -3139,7 +3144,7 @@ const Parser = struct {
3139 },3144 },
3140 }),3145 }),
3141 else => return p.addNode(.{3146 else => return p.addNode(.{
3142 .tag = .switch_case,3147 .tag = if (is_inline) .switch_case_inline else .switch_case,
3143 .main_token = arrow_token,3148 .main_token = arrow_token,
3144 .data = .{3149 .data = .{
3145 .lhs = try p.addExtra(try p.listToSpan(items)),3150 .lhs = try p.addExtra(try p.listToSpan(items)),
lib/std/zig/parser_test.zig+2
...@@ -3276,6 +3276,8 @@ test "zig fmt: switch" {...@@ -3276,6 +3276,8 @@ test "zig fmt: switch" {
3276 \\ switch (u) {3276 \\ switch (u) {
3277 \\ Union.Int => |int| {},3277 \\ Union.Int => |int| {},
3278 \\ Union.Float => |*float| unreachable,3278 \\ Union.Float => |*float| unreachable,
3279 \\ 1 => |a, b| unreachable,
3280 \\ 2 => |*a, b| unreachable,
3279 \\ }3281 \\ }
3280 \\}3282 \\}
3281 \\3283 \\
lib/std/zig/render.zig+15-6
...@@ -685,8 +685,8 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -685,8 +685,8 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
685 return renderToken(ais, tree, tree.lastToken(node), space); // rbrace685 return renderToken(ais, tree, tree.lastToken(node), space); // rbrace
686 },686 },
687687
688 .switch_case_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),688 .switch_case_one, .switch_case_inline_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
689 .switch_case => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),689 .switch_case, .switch_case_inline => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
690690
691 .while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space),691 .while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space),
692 .while_cont => return renderWhile(gpa, ais, tree, tree.whileCont(node), space),692 .while_cont => return renderWhile(gpa, ais, tree, tree.whileCont(node), space),
...@@ -1509,6 +1509,11 @@ fn renderSwitchCase(...@@ -1509,6 +1509,11 @@ fn renderSwitchCase(
1509 break :blk hasComment(tree, tree.firstToken(switch_case.ast.values[0]), switch_case.ast.arrow_token);1509 break :blk hasComment(tree, tree.firstToken(switch_case.ast.values[0]), switch_case.ast.arrow_token);
1510 };1510 };
15111511
1512 // render inline keyword
1513 if (switch_case.inline_token) |some| {
1514 try renderToken(ais, tree, some, .space);
1515 }
1516
1512 // Render everything before the arrow1517 // Render everything before the arrow
1513 if (switch_case.ast.values.len == 0) {1518 if (switch_case.ast.values.len == 0) {
1514 try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword1519 try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword
...@@ -1536,13 +1541,17 @@ fn renderSwitchCase(...@@ -1536,13 +1541,17 @@ fn renderSwitchCase(
15361541
1537 if (switch_case.payload_token) |payload_token| {1542 if (switch_case.payload_token) |payload_token| {
1538 try renderToken(ais, tree, payload_token - 1, .none); // pipe1543 try renderToken(ais, tree, payload_token - 1, .none); // pipe
1544 const ident = payload_token + @boolToInt(token_tags[payload_token] == .asterisk);
1539 if (token_tags[payload_token] == .asterisk) {1545 if (token_tags[payload_token] == .asterisk) {
1540 try renderToken(ais, tree, payload_token, .none); // asterisk1546 try renderToken(ais, tree, payload_token, .none); // asterisk
1541 try renderToken(ais, tree, payload_token + 1, .none); // identifier1547 }
1542 try renderToken(ais, tree, payload_token + 2, pre_target_space); // pipe1548 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
1543 } else {1553 } else {
1544 try renderToken(ais, tree, payload_token, .none); // identifier1554 try renderToken(ais, tree, ident + 1, pre_target_space); // pipe
1545 try renderToken(ais, tree, payload_token + 1, pre_target_space); // pipe
1546 }1555 }
1547 }1556 }
15481557
src/AstGen.zig+118-52
...@@ -386,7 +386,9 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins...@@ -386,7 +386,9 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
386 .simple_var_decl => unreachable,386 .simple_var_decl => unreachable,
387 .aligned_var_decl => unreachable,387 .aligned_var_decl => unreachable,
388 .switch_case => unreachable,388 .switch_case => unreachable,
389 .switch_case_inline => unreachable,
389 .switch_case_one => unreachable,390 .switch_case_one => unreachable,
391 .switch_case_inline_one => unreachable,
390 .container_field_init => unreachable,392 .container_field_init => unreachable,
391 .container_field_align => unreachable,393 .container_field_align => unreachable,
392 .container_field => unreachable,394 .container_field => unreachable,
...@@ -600,7 +602,9 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -600,7 +602,9 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
600 .@"errdefer" => unreachable, // Handled in `blockExpr`.602 .@"errdefer" => unreachable, // Handled in `blockExpr`.
601603
602 .switch_case => unreachable, // Handled in `switchExpr`.604 .switch_case => unreachable, // Handled in `switchExpr`.
605 .switch_case_inline => unreachable, // Handled in `switchExpr`.
603 .switch_case_one => unreachable, // Handled in `switchExpr`.606 .switch_case_one => unreachable, // Handled in `switchExpr`.
607 .switch_case_inline_one => unreachable, // Handled in `switchExpr`.
604 .switch_range => unreachable, // Handled in `switchExpr`.608 .switch_range => unreachable, // Handled in `switchExpr`.
605609
606 .asm_output => unreachable, // Handled in `asmExpr`.610 .asm_output => unreachable, // Handled in `asmExpr`.
...@@ -2369,6 +2373,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2369,6 +2373,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2369 .switch_capture_ref,2373 .switch_capture_ref,
2370 .switch_capture_multi,2374 .switch_capture_multi,
2371 .switch_capture_multi_ref,2375 .switch_capture_multi_ref,
2376 .switch_capture_tag,
2372 .struct_init_empty,2377 .struct_init_empty,
2373 .struct_init,2378 .struct_init,
2374 .struct_init_ref,2379 .struct_init_ref,
...@@ -6213,14 +6218,15 @@ fn switchExpr(...@@ -6213,14 +6218,15 @@ fn switchExpr(
6213 var any_payload_is_ref = false;6218 var any_payload_is_ref = false;
6214 var scalar_cases_len: u32 = 0;6219 var scalar_cases_len: u32 = 0;
6215 var multi_cases_len: u32 = 0;6220 var multi_cases_len: u32 = 0;
6221 var inline_cases_len: u32 = 0;
6216 var special_prong: Zir.SpecialProng = .none;6222 var special_prong: Zir.SpecialProng = .none;
6217 var special_node: Ast.Node.Index = 0;6223 var special_node: Ast.Node.Index = 0;
6218 var else_src: ?Ast.TokenIndex = null;6224 var else_src: ?Ast.TokenIndex = null;
6219 var underscore_src: ?Ast.TokenIndex = null;6225 var underscore_src: ?Ast.TokenIndex = null;
6220 for (case_nodes) |case_node| {6226 for (case_nodes) |case_node| {
6221 const case = switch (node_tags[case_node]) {6227 const case = switch (node_tags[case_node]) {
6222 .switch_case_one => tree.switchCaseOne(case_node),6228 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
6223 .switch_case => tree.switchCase(case_node),6229 .switch_case, .switch_case_inline => tree.switchCase(case_node),
6224 else => unreachable,6230 else => unreachable,
6225 };6231 };
6226 if (case.payload_token) |payload_token| {6232 if (case.payload_token) |payload_token| {
...@@ -6304,6 +6310,9 @@ fn switchExpr(...@@ -6304,6 +6310,9 @@ fn switchExpr(
6304 },6310 },
6305 );6311 );
6306 }6312 }
6313 if (case.inline_token != null) {
6314 return astgen.failTok(case_src, "cannot inline '_' prong", .{});
6315 }
6307 special_node = case_node;6316 special_node = case_node;
6308 special_prong = .under;6317 special_prong = .under;
6309 underscore_src = case_src;6318 underscore_src = case_src;
...@@ -6315,6 +6324,9 @@ fn switchExpr(...@@ -6315,6 +6324,9 @@ fn switchExpr(
6315 } else {6324 } else {
6316 multi_cases_len += 1;6325 multi_cases_len += 1;
6317 }6326 }
6327 if (case.inline_token != null) {
6328 inline_cases_len += 1;
6329 }
6318 }6330 }
63196331
6320 const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;6332 const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
...@@ -6354,8 +6366,8 @@ fn switchExpr(...@@ -6354,8 +6366,8 @@ fn switchExpr(
6354 var scalar_case_index: u32 = 0;6366 var scalar_case_index: u32 = 0;
6355 for (case_nodes) |case_node| {6367 for (case_nodes) |case_node| {
6356 const case = switch (node_tags[case_node]) {6368 const case = switch (node_tags[case_node]) {
6357 .switch_case_one => tree.switchCaseOne(case_node),6369 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
6358 .switch_case => tree.switchCase(case_node),6370 .switch_case, .switch_case_inline => tree.switchCase(case_node),
6359 else => unreachable,6371 else => unreachable,
6360 };6372 };
63616373
...@@ -6364,8 +6376,12 @@ fn switchExpr(...@@ -6364,8 +6376,12 @@ fn switchExpr(
63646376
6365 var dbg_var_name: ?u32 = null;6377 var dbg_var_name: ?u32 = null;
6366 var dbg_var_inst: Zir.Inst.Ref = undefined;6378 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;
6367 var capture_inst: Zir.Inst.Index = 0;6381 var capture_inst: Zir.Inst.Index = 0;
6382 var tag_inst: Zir.Inst.Index = 0;
6368 var capture_val_scope: Scope.LocalVal = undefined;6383 var capture_val_scope: Scope.LocalVal = undefined;
6384 var tag_scope: Scope.LocalVal = undefined;
6369 const sub_scope = blk: {6385 const sub_scope = blk: {
6370 const payload_token = case.payload_token orelse break :blk &case_scope.base;6386 const payload_token = case.payload_token orelse break :blk &case_scope.base;
6371 const ident = if (token_tags[payload_token] == .asterisk)6387 const ident = if (token_tags[payload_token] == .asterisk)
...@@ -6373,59 +6389,96 @@ fn switchExpr(...@@ -6373,59 +6389,96 @@ fn switchExpr(
6373 else6389 else
6374 payload_token;6390 payload_token;
6375 const is_ptr = ident != payload_token;6391 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, "_")) {
6377 if (is_ptr) {6395 if (is_ptr) {
6378 return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});6396 return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
6379 }6397 }
6380 break :blk &case_scope.base;6398 payload_sub_scope = &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 } else {6399 } else {
6399 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);6400 if (case_node == special_node) {
6400 const is_ptr_bits: u2 = @boolToInt(is_ptr);6401 const capture_tag: Zir.Inst.Tag = if (is_ptr)
6401 const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {6402 .switch_capture_ref
6402 0b00 => .switch_capture,6403 else
6403 0b01 => .switch_capture_ref,6404 .switch_capture;
6404 0b10 => .switch_capture_multi,6405 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
6405 0b11 => .switch_capture_multi_ref,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",
6406 };6444 };
6407 const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;6445 dbg_var_name = capture_name;
6408 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);6446 dbg_var_inst = indexToRef(capture_inst);
6409 try astgen.instructions.append(gpa, .{6447 payload_sub_scope = &capture_val_scope.base;
6410 .tag = capture_tag,6448 }
6411 .data = .{ .switch_capture = .{6449
6412 .switch_inst = switch_block,6450 const tag_token = if (token_tags[ident + 1] == .comma)
6413 .prong_index = capture_index,6451 ident + 2
6414 } },6452 else
6415 });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", .{});
6416 }6459 }
6417 const capture_name = try astgen.identAsString(ident);6460 const tag_name = try astgen.identAsString(tag_token);
6418 capture_val_scope = .{6461 try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice);
6419 .parent = &case_scope.base,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,
6420 .gen_zir = &case_scope,6473 .gen_zir = &case_scope,
6421 .name = capture_name,6474 .name = tag_name,
6422 .inst = indexToRef(capture_inst),6475 .inst = indexToRef(tag_inst),
6423 .token_src = payload_token,6476 .token_src = tag_token,
6424 .id_cat = .@"capture",6477 .id_cat = .@"switch tag capture",
6425 };6478 };
6426 dbg_var_name = capture_name;6479 dbg_var_tag_name = tag_name;
6427 dbg_var_inst = indexToRef(capture_inst);6480 dbg_var_tag_inst = indexToRef(tag_inst);
6428 break :blk &capture_val_scope.base;6481 break :blk &tag_scope.base;
6429 };6482 };
64306483
6431 const header_index = @intCast(u32, payloads.items.len);6484 const header_index = @intCast(u32, payloads.items.len);
...@@ -6480,10 +6533,14 @@ fn switchExpr(...@@ -6480,10 +6533,14 @@ fn switchExpr(
6480 defer case_scope.unstack();6533 defer case_scope.unstack();
64816534
6482 if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst);6535 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);
6483 try case_scope.addDbgBlockBegin();6537 try case_scope.addDbgBlockBegin();
6484 if (dbg_var_name) |some| {6538 if (dbg_var_name) |some| {
6485 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);6539 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
6486 }6540 }
6541 if (dbg_var_tag_name) |some| {
6542 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_tag_inst);
6543 }
6487 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);6544 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
6488 try checkUsed(parent_gz, &case_scope.base, sub_scope);6545 try checkUsed(parent_gz, &case_scope.base, sub_scope);
6489 try case_scope.addDbgBlockEnd();6546 try case_scope.addDbgBlockEnd();
...@@ -6495,7 +6552,8 @@ fn switchExpr(...@@ -6495,7 +6552,8 @@ fn switchExpr(
6495 const case_slice = case_scope.instructionsSlice();6552 const case_slice = case_scope.instructionsSlice();
6496 const body_len = astgen.countBodyLenAfterFixups(case_slice);6553 const body_len = astgen.countBodyLenAfterFixups(case_slice);
6497 try payloads.ensureUnusedCapacity(gpa, body_len);6554 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;
6499 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);6557 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
6500 }6558 }
6501 }6559 }
...@@ -6509,7 +6567,6 @@ fn switchExpr(...@@ -6509,7 +6567,6 @@ fn switchExpr(
6509 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{6567 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
6510 .operand = cond,6568 .operand = cond,
6511 .bits = Zir.Inst.SwitchBlock.Bits{6569 .bits = Zir.Inst.SwitchBlock.Bits{
6512 .is_ref = any_payload_is_ref,
6513 .has_multi_cases = multi_cases_len != 0,6570 .has_multi_cases = multi_cases_len != 0,
6514 .has_else = special_prong == .@"else",6571 .has_else = special_prong == .@"else",
6515 .has_under = special_prong == .under,6572 .has_under = special_prong == .under,
...@@ -6543,7 +6600,7 @@ fn switchExpr(...@@ -6543,7 +6600,7 @@ fn switchExpr(
6543 end_index += 3 + items_len + 2 * ranges_len;6600 end_index += 3 + items_len + 2 * ranges_len;
6544 }6601 }
65456602
6546 const body_len = payloads.items[body_len_index];6603 const body_len = @truncate(u31, payloads.items[body_len_index]);
6547 end_index += body_len;6604 end_index += body_len;
65486605
6549 switch (strat.tag) {6606 switch (strat.tag) {
...@@ -8433,7 +8490,9 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_...@@ -8433,7 +8490,9 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_
8433 .@"usingnamespace",8490 .@"usingnamespace",
8434 .test_decl,8491 .test_decl,
8435 .switch_case,8492 .switch_case,
8493 .switch_case_inline,
8436 .switch_case_one,8494 .switch_case_one,
8495 .switch_case_inline_one,
8437 .container_field_init,8496 .container_field_init,
8438 .container_field_align,8497 .container_field_align,
8439 .container_field,8498 .container_field,
...@@ -8665,7 +8724,9 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev...@@ -8665,7 +8724,9 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
8665 .@"usingnamespace",8724 .@"usingnamespace",
8666 .test_decl,8725 .test_decl,
8667 .switch_case,8726 .switch_case,
8727 .switch_case_inline,
8668 .switch_case_one,8728 .switch_case_one,
8729 .switch_case_inline_one,
8669 .container_field_init,8730 .container_field_init,
8670 .container_field_align,8731 .container_field_align,
8671 .container_field,8732 .container_field,
...@@ -8876,7 +8937,9 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In...@@ -8876,7 +8937,9 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
8876 .@"usingnamespace",8937 .@"usingnamespace",
8877 .test_decl,8938 .test_decl,
8878 .switch_case,8939 .switch_case,
8940 .switch_case_inline,
8879 .switch_case_one,8941 .switch_case_one,
8942 .switch_case_inline_one,
8880 .container_field_init,8943 .container_field_init,
8881 .container_field_align,8944 .container_field_align,
8882 .container_field,8945 .container_field,
...@@ -9118,7 +9181,9 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {...@@ -9118,7 +9181,9 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
9118 .@"usingnamespace",9181 .@"usingnamespace",
9119 .test_decl,9182 .test_decl,
9120 .switch_case,9183 .switch_case,
9184 .switch_case_inline,
9121 .switch_case_one,9185 .switch_case_one,
9186 .switch_case_inline_one,
9122 .container_field_init,9187 .container_field_init,
9123 .container_field_align,9188 .container_field_align,
9124 .container_field,9189 .container_field,
...@@ -10051,6 +10116,7 @@ const Scope = struct {...@@ -10051,6 +10116,7 @@ const Scope = struct {
10051 @"local constant",10116 @"local constant",
10052 @"local variable",10117 @"local variable",
10053 @"loop index capture",10118 @"loop index capture",
10119 @"switch tag capture",
10054 @"capture",10120 @"capture",
10055 };10121 };
1005610122
src/Module.zig+8-8
...@@ -2445,8 +2445,8 @@ pub const SrcLoc = struct {...@@ -2445,8 +2445,8 @@ pub const SrcLoc = struct {
2445 const case_nodes = tree.extra_data[extra.start..extra.end];2445 const case_nodes = tree.extra_data[extra.start..extra.end];
2446 for (case_nodes) |case_node| {2446 for (case_nodes) |case_node| {
2447 const case = switch (node_tags[case_node]) {2447 const case = switch (node_tags[case_node]) {
2448 .switch_case_one => tree.switchCaseOne(case_node),2448 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2449 .switch_case => tree.switchCase(case_node),2449 .switch_case, .switch_case_inline => tree.switchCase(case_node),
2450 else => unreachable,2450 else => unreachable,
2451 };2451 };
2452 const is_special = (case.ast.values.len == 0) or2452 const is_special = (case.ast.values.len == 0) or
...@@ -2469,8 +2469,8 @@ pub const SrcLoc = struct {...@@ -2469,8 +2469,8 @@ pub const SrcLoc = struct {
2469 const case_nodes = tree.extra_data[extra.start..extra.end];2469 const case_nodes = tree.extra_data[extra.start..extra.end];
2470 for (case_nodes) |case_node| {2470 for (case_nodes) |case_node| {
2471 const case = switch (node_tags[case_node]) {2471 const case = switch (node_tags[case_node]) {
2472 .switch_case_one => tree.switchCaseOne(case_node),2472 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2473 .switch_case => tree.switchCase(case_node),2473 .switch_case, .switch_case_inline => tree.switchCase(case_node),
2474 else => unreachable,2474 else => unreachable,
2475 };2475 };
2476 const is_special = (case.ast.values.len == 0) or2476 const is_special = (case.ast.values.len == 0) or
...@@ -2491,8 +2491,8 @@ pub const SrcLoc = struct {...@@ -2491,8 +2491,8 @@ pub const SrcLoc = struct {
2491 const case_node = src_loc.declRelativeToNodeIndex(node_off);2491 const case_node = src_loc.declRelativeToNodeIndex(node_off);
2492 const node_tags = tree.nodes.items(.tag);2492 const node_tags = tree.nodes.items(.tag);
2493 const case = switch (node_tags[case_node]) {2493 const case = switch (node_tags[case_node]) {
2494 .switch_case_one => tree.switchCaseOne(case_node),2494 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2495 .switch_case => tree.switchCase(case_node),2495 .switch_case, .switch_case_inline => tree.switchCase(case_node),
2496 else => unreachable,2496 else => unreachable,
2497 };2497 };
2498 const start_tok = case.payload_token.?;2498 const start_tok = case.payload_token.?;
...@@ -5940,8 +5940,8 @@ pub const SwitchProngSrc = union(enum) {...@@ -5940,8 +5940,8 @@ pub const SwitchProngSrc = union(enum) {
5940 var scalar_i: u32 = 0;5940 var scalar_i: u32 = 0;
5941 for (case_nodes) |case_node| {5941 for (case_nodes) |case_node| {
5942 const case = switch (node_tags[case_node]) {5942 const case = switch (node_tags[case_node]) {
5943 .switch_case_one => tree.switchCaseOne(case_node),5943 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
5944 .switch_case => tree.switchCase(case_node),5944 .switch_case, .switch_case_inline => tree.switchCase(case_node),
5945 else => unreachable,5945 else => unreachable,
5946 };5946 };
5947 if (case.ast.values.len == 0)5947 if (case.ast.values.len == 0)
src/Sema.zig+407-124
...@@ -162,6 +162,9 @@ pub const Block = struct {...@@ -162,6 +162,9 @@ pub const Block = struct {
162 /// type of `err` in `else => |err|`162 /// type of `err` in `else => |err|`
163 switch_else_err_ty: ?Type = null,163 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
165 const Param = struct {168 const Param = struct {
166 /// `noreturn` means `anytype`.169 /// `noreturn` means `anytype`.
167 ty: Type,170 ty: Type,
...@@ -603,6 +606,21 @@ fn resolveBody(...@@ -603,6 +606,21 @@ fn resolveBody(
603 return try sema.resolveInst(break_data.operand);606 return try sema.resolveInst(break_data.operand);
604}607}
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
606pub fn analyzeBody(624pub fn analyzeBody(
607 sema: *Sema,625 sema: *Sema,
608 block: *Block,626 block: *Block,
...@@ -796,6 +814,7 @@ fn analyzeBodyInner(...@@ -796,6 +814,7 @@ fn analyzeBodyInner(
796 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),814 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
797 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),815 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
798 .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),816 .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
817 .switch_capture_tag => try sema.zirSwitchCaptureTag(block, inst),
799 .type_info => try sema.zirTypeInfo(block, inst),818 .type_info => try sema.zirTypeInfo(block, inst),
800 .size_of => try sema.zirSizeOf(block, inst),819 .size_of => try sema.zirSizeOf(block, inst),
801 .bit_size_of => try sema.zirBitSizeOf(block, inst),820 .bit_size_of => try sema.zirBitSizeOf(block, inst),
...@@ -9030,13 +9049,38 @@ fn zirSwitchCapture(...@@ -9030,13 +9049,38 @@ fn zirSwitchCapture(
9030 const switch_info = zir_datas[capture_info.switch_inst].pl_node;9049 const switch_info = zir_datas[capture_info.switch_inst].pl_node;
9031 const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);9050 const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);
9032 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };9051 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };
9033 const operand_is_ref = switch_extra.data.bits.is_ref;
9034 const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;9052 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;
9036 const operand_ptr = try sema.resolveInst(cond_info.operand);9056 const operand_ptr = try sema.resolveInst(cond_info.operand);
9037 const operand_ptr_ty = sema.typeOf(operand_ptr);9057 const operand_ptr_ty = sema.typeOf(operand_ptr);
9038 const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;9058 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
9040 const operand = if (operand_is_ref)9084 const operand = if (operand_is_ref)
9041 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)9085 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
9042 else9086 else
...@@ -9045,7 +9089,6 @@ fn zirSwitchCapture(...@@ -9045,7 +9089,6 @@ fn zirSwitchCapture(
9045 if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {9089 if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {
9046 // It is the else/`_` prong.9090 // It is the else/`_` prong.
9047 if (is_ref) {9091 if (is_ref) {
9048 assert(operand_is_ref);
9049 return operand_ptr;9092 return operand_ptr;
9050 }9093 }
90519094
...@@ -9105,8 +9148,6 @@ fn zirSwitchCapture(...@@ -9105,8 +9148,6 @@ fn zirSwitchCapture(
9105 }9148 }
91069149
9107 if (is_ref) {9150 if (is_ref) {
9108 assert(operand_is_ref);
9109
9110 const field_ty_ptr = try Type.ptr(sema.arena, sema.mod, .{9151 const field_ty_ptr = try Type.ptr(sema.arena, sema.mod, .{
9111 .pointee_type = first_field.ty,9152 .pointee_type = first_field.ty,
9112 .@"addrspace" = .generic,9153 .@"addrspace" = .generic,
...@@ -9167,7 +9208,6 @@ fn zirSwitchCapture(...@@ -9167,7 +9208,6 @@ fn zirSwitchCapture(
9167 // In this case the capture value is just the passed-through value of the9208 // In this case the capture value is just the passed-through value of the
9168 // switch condition.9209 // switch condition.
9169 if (is_ref) {9210 if (is_ref) {
9170 assert(operand_is_ref);
9171 return operand_ptr;9211 return operand_ptr;
9172 } else {9212 } else {
9173 return operand;9213 return operand;
...@@ -9176,6 +9216,33 @@ fn zirSwitchCapture(...@@ -9176,6 +9216,33 @@ fn zirSwitchCapture(
9176 }9216 }
9177}9217}
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
9179fn zirSwitchCond(9246fn zirSwitchCond(
9180 sema: *Sema,9247 sema: *Sema,
9181 block: *Block,9248 block: *Block,
...@@ -9273,14 +9340,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9273,14 +9340,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9273 } else 0;9340 } else 0;
92749341
9275 const special_prong = extra.data.bits.specialProng();9342 const special_prong = extra.data.bits.specialProng();
9276 const special: struct { body: []const Zir.Inst.Index, end: usize } = switch (special_prong) {9343 const special: struct { body: []const Zir.Inst.Index, end: usize, is_inline: bool } = switch (special_prong) {
9277 .none => .{ .body = &.{}, .end = header_extra_index },9344 .none => .{ .body = &.{}, .end = header_extra_index, .is_inline = false },
9278 .under, .@"else" => blk: {9345 .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]);
9280 const extra_body_start = header_extra_index + 1;9347 const extra_body_start = header_extra_index + 1;
9281 break :blk .{9348 break :blk .{
9282 .body = sema.code.extra[extra_body_start..][0..body_len],9349 .body = sema.code.extra[extra_body_start..][0..body_len],
9283 .end = extra_body_start + body_len,9350 .end = extra_body_start + body_len,
9351 .is_inline = sema.code.extra[header_extra_index] >> 31 != 0,
9284 };9352 };
9285 },9353 },
9286 };9354 };
...@@ -9292,8 +9360,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9292,8 +9360,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9292 break :blk sema.typeOf(raw_operand);9360 break :blk sema.typeOf(raw_operand);
9293 };9361 };
9294 const union_originally = maybe_union_ty.zigTypeTag() == .Union;9362 const union_originally = maybe_union_ty.zigTypeTag() == .Union;
9295 var seen_union_fields: []?Module.SwitchProngSrc = &.{};9363
9296 defer gpa.free(seen_union_fields);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
9298 var empty_enum = false;9377 var empty_enum = false;
92999378
...@@ -9330,15 +9409,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9330,15 +9409,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9330 switch (operand_ty.zigTypeTag()) {9409 switch (operand_ty.zigTypeTag()) {
9331 .Union => unreachable, // handled in zirSwitchCond9410 .Union => unreachable, // handled in zirSwitchCond
9332 .Enum => {9411 .Enum => {
9333 var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());9412 seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());
9334 empty_enum = seen_fields.len == 0 and !operand_ty.isNonexhaustiveEnum();9413 empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum();
9335 defer if (!union_originally) gpa.free(seen_fields);9414 mem.set(?Module.SwitchProngSrc, seen_enum_fields, null);
9336 if (union_originally) seen_union_fields = seen_fields;9415 // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
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();
93429416
9343 var extra_index: usize = special.end;9417 var extra_index: usize = special.end;
9344 {9418 {
...@@ -9346,13 +9420,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9346,13 +9420,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9346 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {9420 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
9347 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);9421 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
9348 extra_index += 1;9422 extra_index += 1;
9349 const body_len = sema.code.extra[extra_index];9423 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9350 extra_index += 1;9424 extra_index += 1;
9351 extra_index += body_len;9425 extra_index += body_len;
93529426
9353 try sema.validateSwitchItemEnum(9427 try sema.validateSwitchItemEnum(
9354 block,9428 block,
9355 seen_fields,9429 seen_enum_fields,
9356 &range_set,9430 &range_set,
9357 item_ref,9431 item_ref,
9358 src_node_offset,9432 src_node_offset,
...@@ -9367,7 +9441,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9367,7 +9441,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9367 extra_index += 1;9441 extra_index += 1;
9368 const ranges_len = sema.code.extra[extra_index];9442 const ranges_len = sema.code.extra[extra_index];
9369 extra_index += 1;9443 extra_index += 1;
9370 const body_len = sema.code.extra[extra_index];9444 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9371 extra_index += 1;9445 extra_index += 1;
9372 const items = sema.code.refSlice(extra_index, items_len);9446 const items = sema.code.refSlice(extra_index, items_len);
9373 extra_index += items_len + body_len;9447 extra_index += items_len + body_len;
...@@ -9375,7 +9449,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9375,7 +9449,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9375 for (items) |item_ref, item_i| {9449 for (items) |item_ref, item_i| {
9376 try sema.validateSwitchItemEnum(9450 try sema.validateSwitchItemEnum(
9377 block,9451 block,
9378 seen_fields,9452 seen_enum_fields,
9379 &range_set,9453 &range_set,
9380 item_ref,9454 item_ref,
9381 src_node_offset,9455 src_node_offset,
...@@ -9386,7 +9460,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9386,7 +9460,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9386 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);9460 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
9387 }9461 }
9388 }9462 }
9389 const all_tags_handled = for (seen_fields) |seen_src| {9463 const all_tags_handled = for (seen_enum_fields) |seen_src| {
9390 if (seen_src == null) break false;9464 if (seen_src == null) break false;
9391 } else true;9465 } else true;
93929466
...@@ -9406,7 +9480,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9406,7 +9480,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9406 .{},9480 .{},
9407 );9481 );
9408 errdefer msg.destroy(sema.gpa);9482 errdefer msg.destroy(sema.gpa);
9409 for (seen_fields) |seen_src, i| {9483 for (seen_enum_fields) |seen_src, i| {
9410 if (seen_src != null) continue;9484 if (seen_src != null) continue;
94119485
9412 const field_name = operand_ty.enumFieldName(i);9486 const field_name = operand_ty.enumFieldName(i);
...@@ -9437,16 +9511,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9437,16 +9511,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9437 }9511 }
9438 },9512 },
9439 .ErrorSet => {9513 .ErrorSet => {
9440 var seen_errors = SwitchErrorSet.init(gpa);
9441 defer seen_errors.deinit();
9442
9443 var extra_index: usize = special.end;9514 var extra_index: usize = special.end;
9444 {9515 {
9445 var scalar_i: u32 = 0;9516 var scalar_i: u32 = 0;
9446 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {9517 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
9447 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);9518 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
9448 extra_index += 1;9519 extra_index += 1;
9449 const body_len = sema.code.extra[extra_index];9520 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9450 extra_index += 1;9521 extra_index += 1;
9451 extra_index += body_len;9522 extra_index += body_len;
94529523
...@@ -9466,7 +9537,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9466,7 +9537,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9466 extra_index += 1;9537 extra_index += 1;
9467 const ranges_len = sema.code.extra[extra_index];9538 const ranges_len = sema.code.extra[extra_index];
9468 extra_index += 1;9539 extra_index += 1;
9469 const body_len = sema.code.extra[extra_index];9540 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9470 extra_index += 1;9541 extra_index += 1;
9471 const items = sema.code.refSlice(extra_index, items_len);9542 const items = sema.code.refSlice(extra_index, items_len);
9472 extra_index += items_len + body_len;9543 extra_index += items_len + body_len;
...@@ -9579,16 +9650,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9579,16 +9650,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9579 }9650 }
9580 },9651 },
9581 .Int, .ComptimeInt => {9652 .Int, .ComptimeInt => {
9582 var range_set = RangeSet.init(gpa, sema.mod);
9583 defer range_set.deinit();
9584
9585 var extra_index: usize = special.end;9653 var extra_index: usize = special.end;
9586 {9654 {
9587 var scalar_i: u32 = 0;9655 var scalar_i: u32 = 0;
9588 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {9656 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
9589 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);9657 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
9590 extra_index += 1;9658 extra_index += 1;
9591 const body_len = sema.code.extra[extra_index];9659 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9592 extra_index += 1;9660 extra_index += 1;
9593 extra_index += body_len;9661 extra_index += body_len;
95949662
...@@ -9609,7 +9677,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9609,7 +9677,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9609 extra_index += 1;9677 extra_index += 1;
9610 const ranges_len = sema.code.extra[extra_index];9678 const ranges_len = sema.code.extra[extra_index];
9611 extra_index += 1;9679 extra_index += 1;
9612 const body_len = sema.code.extra[extra_index];9680 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9613 extra_index += 1;9681 extra_index += 1;
9614 const items = sema.code.refSlice(extra_index, items_len);9682 const items = sema.code.refSlice(extra_index, items_len);
9615 extra_index += items_len;9683 extra_index += items_len;
...@@ -9677,16 +9745,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9677,16 +9745,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9677 }9745 }
9678 },9746 },
9679 .Bool => {9747 .Bool => {
9680 var true_count: u8 = 0;
9681 var false_count: u8 = 0;
9682
9683 var extra_index: usize = special.end;9748 var extra_index: usize = special.end;
9684 {9749 {
9685 var scalar_i: u32 = 0;9750 var scalar_i: u32 = 0;
9686 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {9751 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
9687 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);9752 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
9688 extra_index += 1;9753 extra_index += 1;
9689 const body_len = sema.code.extra[extra_index];9754 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9690 extra_index += 1;9755 extra_index += 1;
9691 extra_index += body_len;9756 extra_index += body_len;
96929757
...@@ -9707,7 +9772,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9707,7 +9772,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9707 extra_index += 1;9772 extra_index += 1;
9708 const ranges_len = sema.code.extra[extra_index];9773 const ranges_len = sema.code.extra[extra_index];
9709 extra_index += 1;9774 extra_index += 1;
9710 const body_len = sema.code.extra[extra_index];9775 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9711 extra_index += 1;9776 extra_index += 1;
9712 const items = sema.code.refSlice(extra_index, items_len);9777 const items = sema.code.refSlice(extra_index, items_len);
9713 extra_index += items_len + body_len;9778 extra_index += items_len + body_len;
...@@ -9771,7 +9836,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9771,7 +9836,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9771 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {9836 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
9772 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);9837 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
9773 extra_index += 1;9838 extra_index += 1;
9774 const body_len = sema.code.extra[extra_index];9839 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9775 extra_index += 1;9840 extra_index += 1;
9776 extra_index += body_len;9841 extra_index += body_len;
97779842
...@@ -9791,7 +9856,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9791,7 +9856,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9791 extra_index += 1;9856 extra_index += 1;
9792 const ranges_len = sema.code.extra[extra_index];9857 const ranges_len = sema.code.extra[extra_index];
9793 extra_index += 1;9858 extra_index += 1;
9794 const body_len = sema.code.extra[extra_index];9859 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9795 extra_index += 1;9860 extra_index += 1;
9796 const items = sema.code.refSlice(extra_index, items_len);9861 const items = sema.code.refSlice(extra_index, items_len);
9797 extra_index += items_len + body_len;9862 extra_index += items_len + body_len;
...@@ -9871,7 +9936,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9871,7 +9936,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9871 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {9936 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
9872 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);9937 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
9873 extra_index += 1;9938 extra_index += 1;
9874 const body_len = sema.code.extra[extra_index];9939 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9875 extra_index += 1;9940 extra_index += 1;
9876 const body = sema.code.extra[extra_index..][0..body_len];9941 const body = sema.code.extra[extra_index..][0..body_len];
9877 extra_index += body_len;9942 extra_index += body_len;
...@@ -9892,7 +9957,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9892,7 +9957,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9892 extra_index += 1;9957 extra_index += 1;
9893 const ranges_len = sema.code.extra[extra_index];9958 const ranges_len = sema.code.extra[extra_index];
9894 extra_index += 1;9959 extra_index += 1;
9895 const body_len = sema.code.extra[extra_index];9960 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9896 extra_index += 1;9961 extra_index += 1;
9897 const items = sema.code.refSlice(extra_index, items_len);9962 const items = sema.code.refSlice(extra_index, items_len);
9898 extra_index += items_len;9963 extra_index += items_len;
...@@ -9933,7 +9998,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9933,7 +9998,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9933 return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);9998 return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
9934 }9999 }
993510000
9936 if (scalar_cases_len + multi_cases_len == 0) {10001 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
9937 if (empty_enum) {10002 if (empty_enum) {
9938 return Air.Inst.Ref.void_value;10003 return Air.Inst.Ref.void_value;
9939 }10004 }
...@@ -9965,7 +10030,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9965,7 +10030,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9965 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {10030 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
9966 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);10031 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
9967 extra_index += 1;10032 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;
9969 extra_index += 1;10035 extra_index += 1;
9970 const body = sema.code.extra[extra_index..][0..body_len];10036 const body = sema.code.extra[extra_index..][0..body_len];
9971 extra_index += body_len;10037 extra_index += body_len;
...@@ -9975,8 +10041,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9975,8 +10041,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
997510041
9976 case_block.instructions.shrinkRetainingCapacity(0);10042 case_block.instructions.shrinkRetainingCapacity(0);
9977 case_block.wip_capture_scope = wip_captures.scope;10043 case_block.wip_capture_scope = wip_captures.scope;
10044 case_block.inline_case_capture = .none;
997810045
9979 const item = try sema.resolveInst(item_ref);10046 const item = try sema.resolveInst(item_ref);
10047 if (is_inline) case_block.inline_case_capture = item;
9980 // `item` is already guaranteed to be constant known.10048 // `item` is already guaranteed to be constant known.
998110049
9982 const analyze_body = if (union_originally) blk: {10050 const analyze_body = if (union_originally) blk: {
...@@ -9988,18 +10056,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9988,18 +10056,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9988 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {10056 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
9989 // nothing to do here10057 // nothing to do here
9990 } else if (analyze_body) {10058 } else if (analyze_body) {
9991 _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {10059 try sema.analyzeBodyRuntimeBreak(&case_block, body);
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 };
10003 } else {10060 } else {
10004 _ = try case_block.addNoOp(.unreach);10061 _ = try case_block.addNoOp(.unreach);
10005 }10062 }
...@@ -10021,19 +10078,115 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10021,19 +10078,115 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
10021 defer gpa.free(prev_then_body);10078 defer gpa.free(prev_then_body);
1002210079
10023 var cases_len = scalar_cases_len;10080 var cases_len = scalar_cases_len;
10024 var multi_i: usize = 0;10081 var multi_i: u32 = 0;
10025 while (multi_i < multi_cases_len) : (multi_i += 1) {10082 while (multi_i < multi_cases_len) : (multi_i += 1) {
10026 const items_len = sema.code.extra[extra_index];10083 const items_len = sema.code.extra[extra_index];
10027 extra_index += 1;10084 extra_index += 1;
10028 const ranges_len = sema.code.extra[extra_index];10085 const ranges_len = sema.code.extra[extra_index];
10029 extra_index += 1;10086 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;
10031 extra_index += 1;10089 extra_index += 1;
10032 const items = sema.code.refSlice(extra_index, items_len);10090 const items = sema.code.refSlice(extra_index, items_len);
10033 extra_index += items_len;10091 extra_index += items_len;
1003410092
10035 case_block.instructions.shrinkRetainingCapacity(0);10093 case_block.instructions.shrinkRetainingCapacity(0);
10036 case_block.wip_capture_scope = child_block.wip_capture_scope;10094 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
10038 var any_ok: Air.Inst.Ref = .none;10191 var any_ok: Air.Inst.Ref = .none;
1003910192
...@@ -10058,18 +10211,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10058,18 +10211,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
10058 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {10211 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
10059 // nothing to do here10212 // nothing to do here
10060 } else if (analyze_body) {10213 } else if (analyze_body) {
10061 _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {10214 try sema.analyzeBodyRuntimeBreak(&case_block, body);
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 };
10073 } else {10215 } else {
10074 _ = try case_block.addNoOp(.unreach);10216 _ = try case_block.addNoOp(.unreach);
10075 }10217 }
...@@ -10150,18 +10292,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10150,18 +10292,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
10150 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {10292 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
10151 // nothing to do here10293 // nothing to do here
10152 } else {10294 } else {
10153 _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {10295 try sema.analyzeBodyRuntimeBreak(&case_block, body);
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 };
10165 }10296 }
1016610297
10167 try wip_captures.finalize();10298 try wip_captures.finalize();
...@@ -10192,14 +10323,150 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10192,14 +10323,150 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1019210323
10193 var final_else_body: []const Air.Inst.Index = &.{};10324 var final_else_body: []const Air.Inst.Index = &.{};
10194 if (special.body.len != 0 or !is_first or case_block.wantSafety()) {10325 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
10195 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);10461 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
10196 defer wip_captures.deinit();10462 defer wip_captures.deinit();
1019710463
10198 case_block.instructions.shrinkRetainingCapacity(0);10464 case_block.instructions.shrinkRetainingCapacity(0);
10199 case_block.wip_capture_scope = wip_captures.scope;10465 case_block.wip_capture_scope = wip_captures.scope;
10466 case_block.inline_case_capture = .none;
1020010467
10201 const analyze_body = if (union_originally)10468 const analyze_body = if (union_originally and !special.is_inline)
10202 for (seen_union_fields) |seen_field, index| {10469 for (seen_enum_fields) |seen_field, index| {
10203 if (seen_field != null) continue;10470 if (seen_field != null) continue;
10204 const union_obj = maybe_union_ty.cast(Type.Payload.Union).?.data;10471 const union_obj = maybe_union_ty.cast(Type.Payload.Union).?.data;
10205 const field_ty = union_obj.fields.values()[index].ty;10472 const field_ty = union_obj.fields.values()[index].ty;
...@@ -10211,19 +10478,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10211,19 +10478,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
10211 try sema.maybeErrorUnwrap(&case_block, special.body, operand))10478 try sema.maybeErrorUnwrap(&case_block, special.body, operand))
10212 {10479 {
10213 // nothing to do here10480 // nothing to do here
10214 } else if (special.body.len != 0 and analyze_body) {10481 } else if (special.body.len != 0 and analyze_body and !special.is_inline) {
10215 _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) {10482 try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
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 };
10227 } else {10483 } else {
10228 // We still need a terminator in this block, but we have proven10484 // We still need a terminator in this block, but we have proven
10229 // that it is unreachable.10485 // that it is unreachable.
...@@ -10269,6 +10525,55 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10269,6 +10525,55 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
10269 return sema.analyzeBlockBody(block, src, &child_block, merges);10525 return sema.analyzeBlockBody(block, src, &child_block, merges);
10270}10526}
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
10272fn resolveSwitchItemVal(10577fn resolveSwitchItemVal(
10273 sema: *Sema,10578 sema: *Sema,
10274 block: *Block,10579 block: *Block,
...@@ -15351,18 +15656,7 @@ fn zirCondbr(...@@ -15351,18 +15656,7 @@ fn zirCondbr(
15351 sub_block.runtime_index.increment();15656 sub_block.runtime_index.increment();
15352 defer sub_block.instructions.deinit(gpa);15657 defer sub_block.instructions.deinit(gpa);
1535315658
15354 _ = sema.analyzeBodyInner(&sub_block, then_body) catch |err| switch (err) {15659 try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
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 };
15366 const true_instructions = sub_block.instructions.toOwnedSlice(gpa);15660 const true_instructions = sub_block.instructions.toOwnedSlice(gpa);
15367 defer gpa.free(true_instructions);15661 defer gpa.free(true_instructions);
1536815662
...@@ -15381,18 +15675,7 @@ fn zirCondbr(...@@ -15381,18 +15675,7 @@ fn zirCondbr(
15381 if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?)) {15675 if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?)) {
15382 // nothing to do15676 // nothing to do
15383 } else {15677 } else {
15384 _ = sema.analyzeBodyInner(&sub_block, else_body) catch |err| switch (err) {15678 try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
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 };
15396 }15679 }
15397 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +15680 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
15398 true_instructions.len + sub_block.instructions.items.len);15681 true_instructions.len + sub_block.instructions.items.len);
src/Zir.zig+15-12
...@@ -683,6 +683,9 @@ pub const Inst = struct {...@@ -683,6 +683,9 @@ pub const Inst = struct {
683 /// Result is a pointer to the value.683 /// Result is a pointer to the value.
684 /// Uses the `switch_capture` field.684 /// Uses the `switch_capture` field.
685 switch_capture_multi_ref,685 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,
686 /// Given a689 /// Given a
687 /// *A returns *A690 /// *A returns *A
688 /// *E!A returns *A691 /// *E!A returns *A
...@@ -1128,6 +1131,7 @@ pub const Inst = struct {...@@ -1128,6 +1131,7 @@ pub const Inst = struct {
1128 .switch_capture_ref,1131 .switch_capture_ref,
1129 .switch_capture_multi,1132 .switch_capture_multi,
1130 .switch_capture_multi_ref,1133 .switch_capture_multi_ref,
1134 .switch_capture_tag,
1131 .switch_block,1135 .switch_block,
1132 .switch_cond,1136 .switch_cond,
1133 .switch_cond_ref,1137 .switch_cond_ref,
...@@ -1422,6 +1426,7 @@ pub const Inst = struct {...@@ -1422,6 +1426,7 @@ pub const Inst = struct {
1422 .switch_capture_ref,1426 .switch_capture_ref,
1423 .switch_capture_multi,1427 .switch_capture_multi,
1424 .switch_capture_multi_ref,1428 .switch_capture_multi_ref,
1429 .switch_capture_tag,
1425 .switch_block,1430 .switch_block,
1426 .switch_cond,1431 .switch_cond,
1427 .switch_cond_ref,1432 .switch_cond_ref,
...@@ -1681,6 +1686,7 @@ pub const Inst = struct {...@@ -1681,6 +1686,7 @@ pub const Inst = struct {
1681 .switch_capture_ref = .switch_capture,1686 .switch_capture_ref = .switch_capture,
1682 .switch_capture_multi = .switch_capture,1687 .switch_capture_multi = .switch_capture,
1683 .switch_capture_multi_ref = .switch_capture,1688 .switch_capture_multi_ref = .switch_capture,
1689 .switch_capture_tag = .un_tok,
1684 .array_base_ptr = .un_node,1690 .array_base_ptr = .un_node,
1685 .field_base_ptr = .un_node,1691 .field_base_ptr = .un_node,
1686 .validate_array_init_ty = .pl_node,1692 .validate_array_init_ty = .pl_node,
...@@ -2952,12 +2958,9 @@ pub const Inst = struct {...@@ -2952,12 +2958,9 @@ pub const Inst = struct {
2952 has_else: bool,2958 has_else: bool,
2953 /// If true, there is an underscore prong. This is mutually exclusive with `has_else`.2959 /// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
2954 has_under: bool,2960 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,
2958 scalar_cases_len: ScalarCasesLen,2961 scalar_cases_len: ScalarCasesLen,
29592962
2960 pub const ScalarCasesLen = u28;2963 pub const ScalarCasesLen = u29;
29612964
2962 pub fn specialProng(bits: Bits) SpecialProng {2965 pub fn specialProng(bits: Bits) SpecialProng {
2963 const has_else: u2 = @boolToInt(bits.has_else);2966 const has_else: u2 = @boolToInt(bits.has_else);
...@@ -2993,7 +2996,7 @@ pub const Inst = struct {...@@ -2993,7 +2996,7 @@ pub const Inst = struct {
2993 }2996 }
29942997
2995 if (self.bits.specialProng() != .none) {2998 if (self.bits.specialProng() != .none) {
2996 const body_len = zir.extra[extra_index];2999 const body_len = @truncate(u31, zir.extra[extra_index]);
2997 extra_index += 1;3000 extra_index += 1;
2998 const body = zir.extra[extra_index..][0..body_len];3001 const body = zir.extra[extra_index..][0..body_len];
2999 extra_index += body.len;3002 extra_index += body.len;
...@@ -3003,7 +3006,7 @@ pub const Inst = struct {...@@ -3003,7 +3006,7 @@ pub const Inst = struct {
3003 while (true) : (scalar_i += 1) {3006 while (true) : (scalar_i += 1) {
3004 const item = @intToEnum(Ref, zir.extra[extra_index]);3007 const item = @intToEnum(Ref, zir.extra[extra_index]);
3005 extra_index += 1;3008 extra_index += 1;
3006 const body_len = zir.extra[extra_index];3009 const body_len = @truncate(u31, zir.extra[extra_index]);
3007 extra_index += 1;3010 extra_index += 1;
3008 const body = zir.extra[extra_index..][0..body_len];3011 const body = zir.extra[extra_index..][0..body_len];
3009 extra_index += body.len;3012 extra_index += body.len;
...@@ -3032,7 +3035,7 @@ pub const Inst = struct {...@@ -3032,7 +3035,7 @@ pub const Inst = struct {
3032 var extra_index: usize = extra_end + 1;3035 var extra_index: usize = extra_end + 1;
30333036
3034 if (self.bits.specialProng() != .none) {3037 if (self.bits.specialProng() != .none) {
3035 const body_len = zir.extra[extra_index];3038 const body_len = @truncate(u31, zir.extra[extra_index]);
3036 extra_index += 1;3039 extra_index += 1;
3037 const body = zir.extra[extra_index..][0..body_len];3040 const body = zir.extra[extra_index..][0..body_len];
3038 extra_index += body.len;3041 extra_index += body.len;
...@@ -3041,7 +3044,7 @@ pub const Inst = struct {...@@ -3041,7 +3044,7 @@ pub const Inst = struct {
3041 var scalar_i: usize = 0;3044 var scalar_i: usize = 0;
3042 while (scalar_i < self.bits.scalar_cases_len) : (scalar_i += 1) {3045 while (scalar_i < self.bits.scalar_cases_len) : (scalar_i += 1) {
3043 extra_index += 1;3046 extra_index += 1;
3044 const body_len = zir.extra[extra_index];3047 const body_len = @truncate(u31, zir.extra[extra_index]);
3045 extra_index += 1;3048 extra_index += 1;
3046 extra_index += body_len;3049 extra_index += body_len;
3047 }3050 }
...@@ -3049,7 +3052,7 @@ pub const Inst = struct {...@@ -3049,7 +3052,7 @@ pub const Inst = struct {
3049 while (true) : (multi_i += 1) {3052 while (true) : (multi_i += 1) {
3050 const items_len = zir.extra[extra_index];3053 const items_len = zir.extra[extra_index];
3051 extra_index += 2;3054 extra_index += 2;
3052 const body_len = zir.extra[extra_index];3055 const body_len = @truncate(u31, zir.extra[extra_index]);
3053 extra_index += 1;3056 extra_index += 1;
3054 const items = zir.refSlice(extra_index, items_len);3057 const items = zir.refSlice(extra_index, items_len);
3055 extra_index += items_len;3058 extra_index += items_len;
...@@ -3861,7 +3864,7 @@ fn findDeclsSwitch(...@@ -3861,7 +3864,7 @@ fn findDeclsSwitch(
38613864
3862 const special_prong = extra.data.bits.specialProng();3865 const special_prong = extra.data.bits.specialProng();
3863 if (special_prong != .none) {3866 if (special_prong != .none) {
3864 const body_len = zir.extra[extra_index];3867 const body_len = @truncate(u31, zir.extra[extra_index]);
3865 extra_index += 1;3868 extra_index += 1;
3866 const body = zir.extra[extra_index..][0..body_len];3869 const body = zir.extra[extra_index..][0..body_len];
3867 extra_index += body.len;3870 extra_index += body.len;
...@@ -3874,7 +3877,7 @@ fn findDeclsSwitch(...@@ -3874,7 +3877,7 @@ fn findDeclsSwitch(
3874 var scalar_i: usize = 0;3877 var scalar_i: usize = 0;
3875 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {3878 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
3876 extra_index += 1;3879 extra_index += 1;
3877 const body_len = zir.extra[extra_index];3880 const body_len = @truncate(u31, zir.extra[extra_index]);
3878 extra_index += 1;3881 extra_index += 1;
3879 const body = zir.extra[extra_index..][0..body_len];3882 const body = zir.extra[extra_index..][0..body_len];
3880 extra_index += body_len;3883 extra_index += body_len;
...@@ -3889,7 +3892,7 @@ fn findDeclsSwitch(...@@ -3889,7 +3892,7 @@ fn findDeclsSwitch(
3889 extra_index += 1;3892 extra_index += 1;
3890 const ranges_len = zir.extra[extra_index];3893 const ranges_len = zir.extra[extra_index];
3891 extra_index += 1;3894 extra_index += 1;
3892 const body_len = zir.extra[extra_index];3895 const body_len = @truncate(u31, zir.extra[extra_index]);
3893 extra_index += 1;3896 extra_index += 1;
3894 const items = zir.refSlice(extra_index, items_len);3897 const items = zir.refSlice(extra_index, items_len);
3895 extra_index += items_len;3898 extra_index += items_len;
src/arch/x86_64/Emit.zig+1-1
...@@ -2159,7 +2159,7 @@ const RegisterOrMemory = union(enum) {...@@ -2159,7 +2159,7 @@ const RegisterOrMemory = union(enum) {
2159 /// Returns size in bits.2159 /// Returns size in bits.
2160 fn size(reg_or_mem: RegisterOrMemory) u64 {2160 fn size(reg_or_mem: RegisterOrMemory) u64 {
2161 return switch (reg_or_mem) {2161 return switch (reg_or_mem) {
2162 .register => |reg| reg.size(),2162 .register => |register| register.size(),
2163 .memory => |memory| memory.size(),2163 .memory => |memory| memory.size(),
2164 };2164 };
2165 }2165 }
src/print_zir.zig+10-5
...@@ -237,6 +237,7 @@ const Writer = struct {...@@ -237,6 +237,7 @@ const Writer = struct {
237 .ret_tok,237 .ret_tok,
238 .ensure_err_payload_void,238 .ensure_err_payload_void,
239 .closure_capture,239 .closure_capture,
240 .switch_capture_tag,
240 => try self.writeUnTok(stream, inst),241 => try self.writeUnTok(stream, inst),
241242
242 .bool_br_and,243 .bool_br_and,
...@@ -1857,7 +1858,6 @@ const Writer = struct {...@@ -1857,7 +1858,6 @@ const Writer = struct {
1857 } else 0;1858 } else 0;
18581859
1859 try self.writeInstRef(stream, extra.data.operand);1860 try self.writeInstRef(stream, extra.data.operand);
1860 try self.writeFlag(stream, ", ref", extra.data.bits.is_ref);
18611861
1862 self.indent += 2;1862 self.indent += 2;
18631863
...@@ -1869,14 +1869,15 @@ const Writer = struct {...@@ -1869,14 +1869,15 @@ const Writer = struct {
1869 else => break :else_prong,1869 else => break :else_prong,
1870 };1870 };
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 "";
1873 extra_index += 1;1874 extra_index += 1;
1874 const body = self.code.extra[extra_index..][0..body_len];1875 const body = self.code.extra[extra_index..][0..body_len];
1875 extra_index += body.len;1876 extra_index += body.len;
18761877
1877 try stream.writeAll(",\n");1878 try stream.writeAll(",\n");
1878 try stream.writeByteNTimes(' ', self.indent);1879 try stream.writeByteNTimes(' ', self.indent);
1879 try stream.print("{s} => ", .{prong_name});1880 try stream.print("{s}{s} => ", .{ inline_text, prong_name });
1880 try self.writeBracedBody(stream, body);1881 try self.writeBracedBody(stream, body);
1881 }1882 }
18821883
...@@ -1886,13 +1887,15 @@ const Writer = struct {...@@ -1886,13 +1887,15 @@ const Writer = struct {
1886 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {1887 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
1887 const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);1888 const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1888 extra_index += 1;1889 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;
1890 extra_index += 1;1892 extra_index += 1;
1891 const body = self.code.extra[extra_index..][0..body_len];1893 const body = self.code.extra[extra_index..][0..body_len];
1892 extra_index += body_len;1894 extra_index += body_len;
18931895
1894 try stream.writeAll(",\n");1896 try stream.writeAll(",\n");
1895 try stream.writeByteNTimes(' ', self.indent);1897 try stream.writeByteNTimes(' ', self.indent);
1898 if (is_inline) try stream.writeAll("inline ");
1896 try self.writeInstRef(stream, item_ref);1899 try self.writeInstRef(stream, item_ref);
1897 try stream.writeAll(" => ");1900 try stream.writeAll(" => ");
1898 try self.writeBracedBody(stream, body);1901 try self.writeBracedBody(stream, body);
...@@ -1905,13 +1908,15 @@ const Writer = struct {...@@ -1905,13 +1908,15 @@ const Writer = struct {
1905 extra_index += 1;1908 extra_index += 1;
1906 const ranges_len = self.code.extra[extra_index];1909 const ranges_len = self.code.extra[extra_index];
1907 extra_index += 1;1910 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;
1909 extra_index += 1;1913 extra_index += 1;
1910 const items = self.code.refSlice(extra_index, items_len);1914 const items = self.code.refSlice(extra_index, items_len);
1911 extra_index += items_len;1915 extra_index += items_len;
19121916
1913 try stream.writeAll(",\n");1917 try stream.writeAll(",\n");
1914 try stream.writeByteNTimes(' ', self.indent);1918 try stream.writeByteNTimes(' ', self.indent);
1919 if (is_inline) try stream.writeAll("inline ");
19151920
1916 for (items) |item_ref, item_i| {1921 for (items) |item_ref, item_i| {
1917 if (item_i != 0) try stream.writeAll(", ");1922 if (item_i != 0) try stream.writeAll(", ");
src/stage1/all_types.hpp+1
...@@ -1039,6 +1039,7 @@ struct AstNodeSwitchProng {...@@ -1039,6 +1039,7 @@ struct AstNodeSwitchProng {
1039 AstNode *expr;1039 AstNode *expr;
1040 bool var_is_ptr;1040 bool var_is_ptr;
1041 bool any_items_are_range;1041 bool any_items_are_range;
1042 bool is_inline;
1042};1043};
10431044
1044struct AstNodeSwitchRange {1045struct AstNodeSwitchRange {
src/stage1/astgen.cpp+6
...@@ -6987,6 +6987,12 @@ static bool astgen_switch_prong_expr(Stage1AstGen *ag, Scope *scope, AstNode *sw...@@ -6987,6 +6987,12 @@ static bool astgen_switch_prong_expr(Stage1AstGen *ag, Scope *scope, AstNode *sw
6987 assert(switch_node->type == NodeTypeSwitchExpr);6987 assert(switch_node->type == NodeTypeSwitchExpr);
6988 assert(prong_node->type == NodeTypeSwitchProng);6988 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
6990 AstNode *expr_node = prong_node->data.switch_prong.expr;6996 AstNode *expr_node = prong_node->data.switch_prong.expr;
6991 AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol;6997 AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol;
6992 Scope *child_scope;6998 Scope *child_scope;
src/stage1/parser.cpp+11-5
...@@ -2306,17 +2306,17 @@ static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc) {...@@ -2306,17 +2306,17 @@ static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc) {
2306 return Optional<PtrIndexPayload>::some(res);2306 return Optional<PtrIndexPayload>::some(res);
2307}2307}
23082308
2309// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr2309// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
2310static AstNode *ast_parse_switch_prong(ParseContext *pc) {2310static AstNode *ast_parse_switch_prong(ParseContext *pc) {
2311 AstNode *res = ast_parse_switch_case(pc);2311 AstNode *res = ast_parse_switch_case(pc);
2312 if (res == nullptr)2312 if (res == nullptr)
2313 return nullptr;2313 return nullptr;
23142314
2315 expect_token(pc, TokenIdFatArrow);2315 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);
2317 AstNode *expr = ast_expect(pc, ast_parse_assign_expr);2317 AstNode *expr = ast_expect(pc, ast_parse_assign_expr);
23182318
2319 PtrPayload payload;2319 PtrIndexPayload payload;
2320 assert(res->type == NodeTypeSwitchProng);2320 assert(res->type == NodeTypeSwitchProng);
2321 res->data.switch_prong.expr = expr;2321 res->data.switch_prong.expr = expr;
2322 if (opt_payload.unwrap(&payload)) {2322 if (opt_payload.unwrap(&payload)) {
...@@ -2331,9 +2331,11 @@ static AstNode *ast_parse_switch_prong(ParseContext *pc) {...@@ -2331,9 +2331,11 @@ static AstNode *ast_parse_switch_prong(ParseContext *pc) {
2331// <- SwitchItem (COMMA SwitchItem)* COMMA?2331// <- SwitchItem (COMMA SwitchItem)* COMMA?
2332// / KEYWORD_else2332// / KEYWORD_else
2333static AstNode *ast_parse_switch_case(ParseContext *pc) {2333static AstNode *ast_parse_switch_case(ParseContext *pc) {
2334 bool is_inline = eat_token_if(pc, TokenIdKeywordInline) != 0;
2334 AstNode *first = ast_parse_switch_item(pc);2335 AstNode *first = ast_parse_switch_item(pc);
2335 if (first != nullptr) {2336 if (first != nullptr) {
2336 AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeSwitchProng, first);2337 AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeSwitchProng, first);
2338 res->data.switch_prong.is_inline = is_inline;
2337 res->data.switch_prong.items.append(first);2339 res->data.switch_prong.items.append(first);
2338 res->data.switch_prong.any_items_are_range = first->type == NodeTypeSwitchRange;2340 res->data.switch_prong.any_items_are_range = first->type == NodeTypeSwitchRange;
23392341
...@@ -2350,9 +2352,13 @@ static AstNode *ast_parse_switch_case(ParseContext *pc) {...@@ -2350,9 +2352,13 @@ static AstNode *ast_parse_switch_case(ParseContext *pc) {
2350 }2352 }
23512353
2352 TokenIndex else_token = eat_token_if(pc, TokenIdKeywordElse);2354 TokenIndex else_token = eat_token_if(pc, TokenIdKeywordElse);
2353 if (else_token != 0)2355 if (else_token != 0) {
2354 return ast_create_node(pc, NodeTypeSwitchProng, else_token);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;
2356 return nullptr;2362 return nullptr;
2357}2363}
23582364
test/behavior.zig+1
...@@ -182,6 +182,7 @@ test {...@@ -182,6 +182,7 @@ test {
182 _ = @import("behavior/decltest.zig");182 _ = @import("behavior/decltest.zig");
183 _ = @import("behavior/packed_struct_explicit_backing_int.zig");183 _ = @import("behavior/packed_struct_explicit_backing_int.zig");
184 _ = @import("behavior/empty_union.zig");184 _ = @import("behavior/empty_union.zig");
185 _ = @import("behavior/inline_switch.zig");
185 }186 }
186187
187 if (builtin.os.tag != .wasi) {188 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