| author | |
| committer | |
| log | ff534d22676b8a934acf1931f91d70c554a4bdca |
| tree | d04b360f7831f6428d13a85d9d9c0d7c04fc1079 |
| parent | 9d5462dcb5b4b4601bdf2e628b9d80fb74000cb2 |
| parent | 17eea918aee98ca29c3762a7ecd568d2f14f66ef |
| signature |
Implement inline switch cases20 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#} | ||
| 4265 | const std = @import("std"); | ||
| 4266 | const expect = std.testing.expect; | ||
| 4267 | const expectError = std.testing.expectError; | ||
| 4268 | |||
| 4269 | fn 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 | |||
| 4279 | const Struct1 = struct { a: u32, b: ?u32 }; | ||
| 4280 | |||
| 4281 | test "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: | ||
| 4292 | fn 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#} | ||
| 4305 | const std = @import("std"); | ||
| 4306 | const expect = std.testing.expect; | ||
| 4307 | |||
| 4308 | const SliceTypeA = extern struct { | ||
| 4309 | len: usize, | ||
| 4310 | ptr: [*]u32, | ||
| 4311 | }; | ||
| 4312 | const SliceTypeB = extern struct { | ||
| 4313 | ptr: [*]SliceTypeA, | ||
| 4314 | len: usize, | ||
| 4315 | }; | ||
| 4316 | const AnySlice = union(enum) { | ||
| 4317 | a: SliceTypeA, | ||
| 4318 | b: SliceTypeB, | ||
| 4319 | c: []const u8, | ||
| 4320 | d: []AnySlice, | ||
| 4321 | }; | ||
| 4322 | |||
| 4323 | fn 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 | |||
| 4338 | fn 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 | |||
| 4347 | test "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#} | ||
| 4358 | const std = @import("std"); | ||
| 4359 | const expect = std.testing.expect; | ||
| 4360 | |||
| 4361 | const U = union(enum) { | ||
| 4362 | a: u32, | ||
| 4363 | b: f32, | ||
| 4364 | }; | ||
| 4365 | |||
| 4366 | fn 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 | |||
| 4379 | test "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#} |
| 4259 | 4387 | ||
| 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 | }, | ||
| 651 | 663 | ||
| 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, |
| 769 | 783 | ||
| ... | @@ -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 | } |
| 1760 | 1774 | ||
| 1761 | pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase { | 1775 | 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,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 | } |
| 1770 | 1784 | ||
| 1771 | pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm { | 1785 | pub 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 | } |
| 2040 | 2054 | ||
| 2041 | fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components) full.SwitchCase { | 2055 | fn 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 | } |
| 2052 | 2072 | ||
| ... | @@ -2454,6 +2474,7 @@ pub const full = struct { | ... | @@ -2454,6 +2474,7 @@ pub const full = struct { |
| 2454 | }; | 2474 | }; |
| 2455 | 2475 | ||
| 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 or | 2478 | /// 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 | } |
| 3102 | 3102 | ||
| 3103 | /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr | 3103 | /// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr |
| 3104 | /// SwitchCase | 3104 | /// SwitchCase |
| 3105 | /// <- SwitchItem (COMMA SwitchItem)* COMMA? | 3105 | /// <- SwitchItem (COMMA SwitchItem)* COMMA? |
| 3106 | /// / KEYWORD_else | 3106 | /// / 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); |
| 3110 | 3110 | ||
| 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(); |
| 3122 | 3127 | ||
| 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); // rbrace | 685 | return renderToken(ais, tree, tree.lastToken(node), space); // rbrace |
| 686 | }, | 686 | }, |
| 687 | 687 | ||
| 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), |
| 690 | 690 | ||
| 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 | }; |
| 1511 | 1511 | ||
| 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 arrow | 1517 | // 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 keyword | 1519 | 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( |
| 1536 | 1541 | ||
| 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); // pipe | 1543 | 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); // asterisk | 1546 | try renderToken(ais, tree, payload_token, .none); // asterisk |
| 1541 | try renderToken(ais, tree, payload_token + 1, .none); // identifier | 1547 | } |
| 1542 | try renderToken(ais, tree, payload_token + 2, pre_target_space); // pipe | 1548 | try renderToken(ais, tree, ident, .none); // identifier |
| 1549 | if (token_tags[ident + 1] == .comma) { | ||
| 1550 | try renderToken(ais, tree, ident + 1, .space); // , | ||
| 1551 | try renderToken(ais, tree, ident + 2, .none); // identifier | ||
| 1552 | try renderToken(ais, tree, ident + 3, pre_target_space); // pipe | ||
| 1543 | } else { | 1553 | } else { |
| 1544 | try renderToken(ais, tree, payload_token, .none); // identifier | 1554 | 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 | } |
| 1548 | 1557 |
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`. |
| 601 | 603 | ||
| 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`. |
| 605 | 609 | ||
| 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 | } |
| 6319 | 6331 | ||
| 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 | }; |
| 6361 | 6373 | ||
| ... | @@ -6364,8 +6376,12 @@ fn switchExpr( | ... | @@ -6364,8 +6376,12 @@ fn switchExpr( |
| 6364 | 6376 | ||
| 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 | else | 6389 | 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 | }; |
| 6430 | 6483 | ||
| 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(); |
| 6481 | 6534 | ||
| 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 | } |
| 6545 | 6602 | ||
| 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; |
| 6548 | 6605 | ||
| 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 | }; |
| 10056 | 10122 |
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) or | 2452 | 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) or | 2476 | 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, |
| 164 | 164 | ||
| 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 | } |
| 605 | 608 | ||
| 609 | fn 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 | |||
| 606 | pub fn analyzeBody( | 624 | pub 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; |
| 9039 | 9059 | ||
| 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 | else | 9086 | 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 | } |
| 9051 | 9094 | ||
| ... | @@ -9105,8 +9148,6 @@ fn zirSwitchCapture( | ... | @@ -9105,8 +9148,6 @@ fn zirSwitchCapture( |
| 9105 | } | 9148 | } |
| 9106 | 9149 | ||
| 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 the | 9208 | // 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 | } |
| 9178 | 9218 | ||
| 9219 | fn 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 | |||
| 9179 | fn zirSwitchCond( | 9246 | fn 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; |
| 9274 | 9341 | ||
| 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 | } | ||
| 9297 | 9376 | ||
| 9298 | var empty_enum = false; | 9377 | var empty_enum = false; |
| 9299 | 9378 | ||
| ... | @@ -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 zirSwitchCond | 9410 | .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(); | ||
| 9342 | 9416 | ||
| 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; |
| 9352 | 9426 | ||
| 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; |
| 9392 | 9466 | ||
| ... | @@ -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; |
| 9411 | 9485 | ||
| 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; |
| 9452 | 9523 | ||
| ... | @@ -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; |
| 9594 | 9662 | ||
| ... | @@ -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; |
| 9692 | 9757 | ||
| ... | @@ -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; |
| 9777 | 9842 | ||
| ... | @@ -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 | } |
| 9935 | 10000 | ||
| 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 |
| 9975 | 10041 | ||
| 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; | ||
| 9978 | 10045 | ||
| 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. |
| 9981 | 10049 | ||
| 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 here | 10057 | // 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); |
| 10022 | 10079 | ||
| 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; |
| 10034 | 10092 | ||
| 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 | } | ||
| 10037 | 10190 | ||
| 10038 | var any_ok: Air.Inst.Ref = .none; | 10191 | var any_ok: Air.Inst.Ref = .none; |
| 10039 | 10192 | ||
| ... | @@ -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 here | 10212 | // 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 here | 10293 | // 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 | } |
| 10166 | 10297 | ||
| 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 |
| 10192 | 10323 | ||
| 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(); |
| 10197 | 10463 | ||
| 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; | ||
| 10200 | 10467 | ||
| 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 here | 10480 | // 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 proven | 10484 | // 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 | } |
| 10271 | 10527 | ||
| 10528 | const 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 | |||
| 10272 | fn resolveSwitchItemVal( | 10577 | fn 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); |
| 15353 | 15658 | ||
| 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); |
| 15368 | 15662 | ||
| ... | @@ -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 do | 15676 | // 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 a | 689 | /// Given a |
| 687 | /// *A returns *A | 690 | /// *A returns *A |
| 688 | /// *E!A returns *A | 691 | /// *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, |
| 2959 | 2962 | ||
| 2960 | pub const ScalarCasesLen = u28; | 2963 | pub const ScalarCasesLen = u29; |
| 2961 | 2964 | ||
| 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 | } |
| 2994 | 2997 | ||
| 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; |
| 3033 | 3036 | ||
| 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( |
| 3861 | 3864 | ||
| 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), |
| 241 | 242 | ||
| 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; |
| 1858 | 1859 | ||
| 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); | ||
| 1861 | 1861 | ||
| 1862 | self.indent += 2; | 1862 | self.indent += 2; |
| 1863 | 1863 | ||
| ... | @@ -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 | }; |
| 1871 | 1871 | ||
| 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; |
| 1876 | 1877 | ||
| 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 | } |
| 1882 | 1883 | ||
| ... | @@ -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; |
| 1893 | 1895 | ||
| 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; |
| 1912 | 1916 | ||
| 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 "); | ||
| 1915 | 1920 | ||
| 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 | }; |
| 1043 | 1044 | ||
| 1044 | struct AstNodeSwitchRange { | 1045 | struct 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); |
| 6989 | 6989 | ||
| 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 | } |
| 2308 | 2308 | ||
| 2309 | // SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr | 2309 | // SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr |
| 2310 | static AstNode *ast_parse_switch_prong(ParseContext *pc) { | 2310 | static 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; |
| 2314 | 2314 | ||
| 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); |
| 2318 | 2318 | ||
| 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_else | 2332 | // / KEYWORD_else |
| 2333 | static AstNode *ast_parse_switch_case(ParseContext *pc) { | 2333 | static 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; |
| 2339 | 2341 | ||
| ... | @@ -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 | } |
| 2351 | 2353 | ||
| 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 | } | ||
| 2355 | 2360 | ||
| 2361 | if (is_inline) pc->current_token -= 1; | ||
| 2356 | return nullptr; | 2362 | return nullptr; |
| 2357 | } | 2363 | } |
| 2358 | 2364 |
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 | } |
| 186 | 187 | ||
| 187 | if (builtin.os.tag != .wasi) { | 188 | if (builtin.os.tag != .wasi) { |
test/behavior/inline_switch.zig created+131| ... | @@ -0,0 +1,131 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | const builtin = @import("builtin"); | ||
| 4 | |||
| 5 | test "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 | |||
| 19 | test "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 | |||
| 31 | const E = enum { a, b, c, d }; | ||
| 32 | test "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 | |||
| 42 | const U = union(E) { a: void, b: u2, c: u3, d: u4 }; | ||
| 43 | test "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 | |||
| 69 | test "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 | |||
| 79 | test "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 | |||
| 90 | test "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 | |||
| 102 | test "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 | |||
| 118 | test "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 @@ | ||
| 1 | const E = enum(u8) { a, b, c, d, _ }; | ||
| 2 | pub 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 @@ | ||
| 1 | pub export fn entry1() void { | ||
| 2 | var a: anyerror = undefined; | ||
| 3 | switch (a) { | ||
| 4 | inline else => {}, | ||
| 5 | } | ||
| 6 | } | ||
| 7 | const E = enum(u8) { a, _ }; | ||
| 8 | pub export fn entry2() void { | ||
| 9 | var a: E = undefined; | ||
| 10 | switch (a) { | ||
| 11 | inline else => {}, | ||
| 12 | } | ||
| 13 | } | ||
| 14 | pub 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 @@ | ||
| 1 | const E = enum { a, b, c, d }; | ||
| 2 | pub 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 @@ | ||
| 1 | const E = enum { a, b, c, d }; | ||
| 2 | pub 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 | ||