Skip to content

Commit c97a209

Browse files
committed
Add support for both '_' and 'else' prongs at the same time in switch statements
If both are used, 'else' handles named members and '_' handles unnamed members. In this case the 'else' prong will be unrolled to an explicit case containing all remaining named values.
1 parent 77df0b9 commit c97a209

File tree

10 files changed

+772
-341
lines changed

10 files changed

+772
-341
lines changed

lib/std/zig/AstGen.zig

Lines changed: 77 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -7652,11 +7652,12 @@ fn switchExpr(
76527652
var scalar_cases_len: u32 = 0;
76537653
var multi_cases_len: u32 = 0;
76547654
var inline_cases_len: u32 = 0;
7655-
var special_prong: Zir.SpecialProng = .none;
7656-
var special_node: Ast.Node.OptionalIndex = .none;
7655+
var else_case_node: Ast.Node.OptionalIndex = .none;
76577656
var else_src: ?Ast.TokenIndex = null;
7658-
var underscore_src: ?Ast.TokenIndex = null;
7657+
var underscore_case_node: Ast.Node.OptionalIndex = .none;
76597658
var underscore_node: Ast.Node.OptionalIndex = .none;
7659+
var underscore_src: ?Ast.TokenIndex = null;
7660+
var underscore_additional_items: Zir.SpecialProngs.AdditionalItems = .none;
76607661
for (case_nodes) |case_node| {
76617662
const case = tree.fullSwitchCase(case_node).?;
76627663
if (case.payload_token) |payload_token| {
@@ -7677,6 +7678,7 @@ fn switchExpr(
76777678
any_non_inline_capture = true;
76787679
}
76797680
}
7681+
76807682
// Check for else prong.
76817683
if (case.ast.values.len == 0) {
76827684
const case_src = case.ast.arrow_token - 1;
@@ -7693,40 +7695,21 @@ fn switchExpr(
76937695
),
76947696
},
76957697
);
7696-
} else if (underscore_src) |some_underscore| {
7697-
return astgen.failNodeNotes(
7698-
node,
7699-
"else and '_' prong in switch expression",
7700-
.{},
7701-
&[_]u32{
7702-
try astgen.errNoteTok(
7703-
case_src,
7704-
"else prong here",
7705-
.{},
7706-
),
7707-
try astgen.errNoteTok(
7708-
some_underscore,
7709-
"'_' prong here",
7710-
.{},
7711-
),
7712-
},
7713-
);
77147698
}
7715-
special_node = case_node.toOptional();
7716-
special_prong = .@"else";
7699+
else_case_node = case_node.toOptional();
77177700
else_src = case_src;
77187701
continue;
77197702
}
77207703

77217704
// Check for '_' prong.
7722-
var found_underscore = false;
7705+
var case_has_underscore = false;
77237706
for (case.ast.values) |val| {
77247707
switch (tree.nodeTag(val)) {
77257708
.identifier => if (mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_")) {
7726-
const case_src = case.ast.arrow_token - 1;
7709+
const val_src = tree.nodeMainToken(val);
77277710
if (underscore_src) |src| {
77287711
return astgen.failTokNotes(
7729-
case_src,
7712+
val_src,
77307713
"multiple '_' prongs in switch expression",
77317714
.{},
77327715
&[_]u32{
@@ -7737,39 +7720,26 @@ fn switchExpr(
77377720
),
77387721
},
77397722
);
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-
);
77587723
}
77597724
if (case.inline_token != null) {
7760-
return astgen.failTok(case_src, "cannot inline '_' prong", .{});
7725+
return astgen.failTok(val_src, "cannot inline '_' prong", .{});
77617726
}
7762-
special_node = case_node.toOptional();
7763-
special_prong = if (case.ast.values.len == 1) .under else .absorbing_under;
7764-
underscore_src = case_src;
7727+
underscore_case_node = case_node.toOptional();
7728+
underscore_src = val_src;
77657729
underscore_node = val.toOptional();
7766-
found_underscore = true;
7730+
underscore_additional_items = switch (case.ast.values.len) {
7731+
0 => unreachable,
7732+
1 => .none,
7733+
2 => .one,
7734+
else => .many,
7735+
};
7736+
case_has_underscore = true;
77677737
},
77687738
.string_literal => return astgen.failNode(val, "cannot switch on strings", .{}),
77697739
else => {},
77707740
}
77717741
}
7772-
if (found_underscore) continue;
7742+
if (case_has_underscore) continue;
77737743

77747744
if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) {
77757745
scalar_cases_len += 1;
@@ -7781,6 +7751,14 @@ fn switchExpr(
77817751
}
77827752
}
77837753

7754+
const special_prongs: Zir.SpecialProngs = .init(
7755+
else_src != null,
7756+
underscore_src != null,
7757+
underscore_additional_items,
7758+
);
7759+
const has_else = special_prongs.hasElse();
7760+
const has_under = special_prongs.hasUnder();
7761+
77847762
const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
77857763

77867764
astgen.advanceSourceCursorToNode(operand_node);
@@ -7801,7 +7779,9 @@ fn switchExpr(
78017779
const payloads = &astgen.scratch;
78027780
const scratch_top = astgen.scratch.items.len;
78037781
const case_table_start = scratch_top;
7804-
const scalar_case_table = case_table_start + @intFromBool(special_prong != .none);
7782+
const else_case_index = if (has_else) case_table_start else undefined;
7783+
const under_case_index = if (has_under) case_table_start + @intFromBool(has_else) else undefined;
7784+
const scalar_case_table = case_table_start + @intFromBool(has_else) + @intFromBool(has_under);
78057785
const multi_case_table = scalar_case_table + scalar_cases_len;
78067786
const case_table_end = multi_case_table + multi_cases_len;
78077787
try astgen.scratch.resize(gpa, case_table_end);
@@ -7933,9 +7913,19 @@ fn switchExpr(
79337913

79347914
const header_index: u32 = @intCast(payloads.items.len);
79357915
const body_len_index = if (is_multi_case) blk: {
7936-
if (case_node.toOptional() == special_node) {
7937-
assert(special_prong == .absorbing_under);
7938-
payloads.items[case_table_start] = header_index;
7916+
if (case_node.toOptional() == underscore_case_node) {
7917+
payloads.items[under_case_index] = header_index;
7918+
if (special_prongs.hasOneAdditionalItem()) {
7919+
try payloads.resize(gpa, header_index + 2); // item, body_len
7920+
const maybe_item_node = case.ast.values[0];
7921+
const item_node = if (maybe_item_node.toOptional() == underscore_node)
7922+
case.ast.values[1]
7923+
else
7924+
maybe_item_node;
7925+
const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
7926+
payloads.items[header_index] = @intFromEnum(item_inst);
7927+
break :blk header_index + 1;
7928+
}
79397929
} else {
79407930
payloads.items[multi_case_table + multi_case_index] = header_index;
79417931
multi_case_index += 1;
@@ -7975,9 +7965,13 @@ fn switchExpr(
79757965
payloads.items[header_index] = items_len;
79767966
payloads.items[header_index + 1] = ranges_len;
79777967
break :blk header_index + 2;
7978-
} else if (case_node.toOptional() == special_node) blk: {
7979-
assert(special_prong != .absorbing_under);
7980-
payloads.items[case_table_start] = header_index;
7968+
} else if (case_node.toOptional() == else_case_node) blk: {
7969+
payloads.items[else_case_index] = header_index;
7970+
try payloads.resize(gpa, header_index + 1); // body_len
7971+
break :blk header_index;
7972+
} else if (case_node.toOptional() == underscore_case_node) blk: {
7973+
assert(!special_prongs.hasAdditionalItems());
7974+
payloads.items[under_case_index] = header_index;
79817975
try payloads.resize(gpa, header_index + 1); // body_len
79827976
break :blk header_index;
79837977
} else blk: {
@@ -8038,7 +8032,7 @@ fn switchExpr(
80388032
.operand = raw_operand,
80398033
.bits = Zir.Inst.SwitchBlock.Bits{
80408034
.has_multi_cases = multi_cases_len != 0,
8041-
.special_prong = special_prong,
8035+
.special_prongs = special_prongs,
80428036
.any_has_tag_capture = any_has_tag_capture,
80438037
.any_non_inline_capture = any_non_inline_capture,
80448038
.has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue,
@@ -8057,29 +8051,40 @@ fn switchExpr(
80578051
const zir_datas = astgen.instructions.items(.data);
80588052
zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
80598053

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];
8054+
if (has_else) {
8055+
const start_index = payloads.items[else_case_index];
8056+
var end_index = start_index + 1;
8057+
const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[start_index]);
8058+
end_index += prong_info.body_len;
8059+
astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
8060+
}
8061+
if (has_under) {
8062+
const start_index = payloads.items[under_case_index];
80658063
var body_len_index = start_index;
80668064
var end_index = start_index;
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 {
8073-
end_index += 1;
8065+
switch (underscore_additional_items) {
8066+
.none => {
8067+
end_index += 1;
8068+
},
8069+
.one => {
8070+
body_len_index += 1;
8071+
end_index += 2;
8072+
},
8073+
.many => {
8074+
body_len_index += 2;
8075+
const items_len = payloads.items[start_index];
8076+
const ranges_len = payloads.items[start_index + 1];
8077+
end_index += 3 + items_len + 2 * ranges_len;
8078+
},
80748079
}
80758080
const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
80768081
end_index += prong_info.body_len;
80778082
astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
80788083
}
8079-
for (payloads.items[normal_case_table_start..case_table_end], 0..) |start_index, i| {
8084+
for (payloads.items[scalar_case_table..case_table_end], 0..) |start_index, i| {
80808085
var body_len_index = start_index;
80818086
var end_index = start_index;
8082-
const table_index = normal_case_table_start + i;
8087+
const table_index = scalar_case_table + i;
80838088
if (table_index < multi_case_table) {
80848089
body_len_index += 1;
80858090
end_index += 2;

0 commit comments

Comments
 (0)