@@ -7656,6 +7656,7 @@ fn switchExpr(
7656
7656
var special_node: Ast.Node.OptionalIndex = .none;
7657
7657
var else_src: ?Ast.TokenIndex = null;
7658
7658
var underscore_src: ?Ast.TokenIndex = null;
7659
+ var underscore_node: Ast.Node.OptionalIndex = .none;
7659
7660
for (case_nodes) |case_node| {
7660
7661
const case = tree.fullSwitchCase(case_node).?;
7661
7662
if (case.payload_token) |payload_token| {
@@ -7676,7 +7677,7 @@ fn switchExpr(
7676
7677
any_non_inline_capture = true;
7677
7678
}
7678
7679
}
7679
- // Check for else/`_` prong.
7680
+ // Check for else prong.
7680
7681
if (case.ast.values.len == 0) {
7681
7682
const case_src = case.ast.arrow_token - 1;
7682
7683
if (else_src) |src| {
@@ -7715,56 +7716,60 @@ fn switchExpr(
7715
7716
special_prong = .@"else";
7716
7717
else_src = case_src;
7717
7718
continue;
7718
- } else if (case.ast.values.len == 1 and
7719
- tree.nodeTag(case.ast.values[0]) == .identifier and
7720
- mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_"))
7721
- {
7722
- const case_src = case.ast.arrow_token - 1;
7723
- if (underscore_src) |src| {
7724
- return astgen.failTokNotes(
7725
- case_src,
7726
- "multiple '_' prongs in switch expression",
7727
- .{},
7728
- &[_]u32{
7729
- try astgen.errNoteTok(
7730
- src,
7731
- "previous '_' prong here",
7732
- .{},
7733
- ),
7734
- },
7735
- );
7736
- } else if (else_src) |some_else| {
7737
- return astgen.failNodeNotes(
7738
- node,
7739
- "else and '_' prong in switch expression",
7740
- .{},
7741
- &[_]u32{
7742
- try astgen.errNoteTok(
7743
- some_else,
7744
- "else prong here",
7745
- .{},
7746
- ),
7747
- try astgen.errNoteTok(
7748
- case_src,
7749
- "'_' prong here",
7750
- .{},
7751
- ),
7752
- },
7753
- );
7754
- }
7755
- if (case.inline_token != null) {
7756
- return astgen.failTok(case_src, "cannot inline '_' prong", .{});
7757
- }
7758
- special_node = case_node.toOptional();
7759
- special_prong = .under;
7760
- underscore_src = case_src;
7761
- continue;
7762
7719
}
7763
7720
7721
+ // Check for '_' prong.
7722
+ var found_underscore = false;
7764
7723
for (case.ast.values) |val| {
7765
- if (tree.nodeTag(val) == .string_literal)
7766
- return astgen.failNode(val, "cannot switch on strings", .{});
7724
+ switch (tree.nodeTag(val)) {
7725
+ .identifier => if (mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_")) {
7726
+ const case_src = case.ast.arrow_token - 1;
7727
+ if (underscore_src) |src| {
7728
+ return astgen.failTokNotes(
7729
+ case_src,
7730
+ "multiple '_' prongs in switch expression",
7731
+ .{},
7732
+ &[_]u32{
7733
+ try astgen.errNoteTok(
7734
+ src,
7735
+ "previous '_' prong here",
7736
+ .{},
7737
+ ),
7738
+ },
7739
+ );
7740
+ } else if (else_src) |some_else| {
7741
+ return astgen.failNodeNotes(
7742
+ node,
7743
+ "else and '_' prong in switch expression",
7744
+ .{},
7745
+ &[_]u32{
7746
+ try astgen.errNoteTok(
7747
+ some_else,
7748
+ "else prong here",
7749
+ .{},
7750
+ ),
7751
+ try astgen.errNoteTok(
7752
+ case_src,
7753
+ "'_' prong here",
7754
+ .{},
7755
+ ),
7756
+ },
7757
+ );
7758
+ }
7759
+ if (case.inline_token != null) {
7760
+ return astgen.failTok(case_src, "cannot inline '_' prong", .{});
7761
+ }
7762
+ special_node = case_node.toOptional();
7763
+ special_prong = if (case.ast.values.len == 1) .under else .absorbing_under;
7764
+ underscore_src = case_src;
7765
+ underscore_node = val.toOptional();
7766
+ found_underscore = true;
7767
+ },
7768
+ .string_literal => return astgen.failNode(val, "cannot switch on strings", .{}),
7769
+ else => {},
7770
+ }
7767
7771
}
7772
+ if (found_underscore) continue;
7768
7773
7769
7774
if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) {
7770
7775
scalar_cases_len += 1;
@@ -7928,14 +7933,23 @@ fn switchExpr(
7928
7933
7929
7934
const header_index: u32 = @intCast(payloads.items.len);
7930
7935
const body_len_index = if (is_multi_case) blk: {
7931
- payloads.items[multi_case_table + multi_case_index] = header_index;
7932
- multi_case_index += 1;
7936
+ if (case_node.toOptional() == special_node) {
7937
+ assert(special_prong == .absorbing_under);
7938
+ payloads.items[case_table_start] = header_index;
7939
+ } else {
7940
+ payloads.items[multi_case_table + multi_case_index] = header_index;
7941
+ multi_case_index += 1;
7942
+ }
7933
7943
try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
7934
7944
7935
7945
// items
7936
7946
var items_len: u32 = 0;
7937
7947
for (case.ast.values) |item_node| {
7938
- if (tree.nodeTag(item_node) == .switch_range) continue;
7948
+ if (item_node.toOptional() == underscore_node or
7949
+ tree.nodeTag(item_node) == .switch_range)
7950
+ {
7951
+ continue;
7952
+ }
7939
7953
items_len += 1;
7940
7954
7941
7955
const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
@@ -7945,7 +7959,9 @@ fn switchExpr(
7945
7959
// ranges
7946
7960
var ranges_len: u32 = 0;
7947
7961
for (case.ast.values) |range| {
7948
- if (tree.nodeTag(range) != .switch_range) continue;
7962
+ if (tree.nodeTag(range) != .switch_range) {
7963
+ continue;
7964
+ }
7949
7965
ranges_len += 1;
7950
7966
7951
7967
const first_node, const last_node = tree.nodeData(range).node_and_node;
@@ -7960,6 +7976,7 @@ fn switchExpr(
7960
7976
payloads.items[header_index + 1] = ranges_len;
7961
7977
break :blk header_index + 2;
7962
7978
} else if (case_node.toOptional() == special_node) blk: {
7979
+ assert(special_prong != .absorbing_under);
7963
7980
payloads.items[case_table_start] = header_index;
7964
7981
try payloads.resize(gpa, header_index + 1); // body_len
7965
7982
break :blk header_index;
@@ -8015,15 +8032,13 @@ fn switchExpr(
8015
8032
try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len +
8016
8033
@intFromBool(multi_cases_len != 0) +
8017
8034
@intFromBool(any_has_tag_capture) +
8018
- payloads.items.len - case_table_end +
8019
- (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len);
8035
+ payloads.items.len - scratch_top);
8020
8036
8021
8037
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
8022
8038
.operand = raw_operand,
8023
8039
.bits = Zir.Inst.SwitchBlock.Bits{
8024
8040
.has_multi_cases = multi_cases_len != 0,
8025
- .has_else = special_prong == .@"else",
8026
- .has_under = special_prong == .under,
8041
+ .special_prong = special_prong,
8027
8042
.any_has_tag_capture = any_has_tag_capture,
8028
8043
.any_non_inline_capture = any_non_inline_capture,
8029
8044
.has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue,
@@ -8042,13 +8057,30 @@ fn switchExpr(
8042
8057
const zir_datas = astgen.instructions.items(.data);
8043
8058
zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
8044
8059
8045
- for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
8060
+ var normal_case_table_start = case_table_start;
8061
+ if (special_prong != .none) {
8062
+ normal_case_table_start += 1;
8063
+
8064
+ const start_index = payloads.items[case_table_start];
8046
8065
var body_len_index = start_index;
8047
8066
var end_index = start_index;
8048
- const table_index = case_table_start + i;
8049
- if (table_index < scalar_case_table) {
8067
+ if (special_prong == .absorbing_under) {
8068
+ body_len_index += 2;
8069
+ const items_len = payloads.items[start_index];
8070
+ const ranges_len = payloads.items[start_index + 1];
8071
+ end_index += 3 + items_len + 2 * ranges_len;
8072
+ } else {
8050
8073
end_index += 1;
8051
- } else if (table_index < multi_case_table) {
8074
+ }
8075
+ const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
8076
+ end_index += prong_info.body_len;
8077
+ astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
8078
+ }
8079
+ for (payloads.items[normal_case_table_start..case_table_end], 0..) |start_index, i| {
8080
+ var body_len_index = start_index;
8081
+ var end_index = start_index;
8082
+ const table_index = normal_case_table_start + i;
8083
+ if (table_index < multi_case_table) {
8052
8084
body_len_index += 1;
8053
8085
end_index += 2;
8054
8086
} else {
0 commit comments