aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorJustus Klausecker <justus@klausecker.de>2025-07-10 01:58:02 +0200
committerJustus Klausecker <justus@klausecker.de>2025-08-07 13:58:47 +0200
commitba549a7d67d0268cdbd4b23a5b7371de4f2d8d33 (patch)
tree2ee5ed3a9546923fa41abeac193634f88f944d82 /lib/std
parent1d9b1c021273037efea6623e669800ad538c8075 (diff)
downloadzig-ba549a7d67d0268cdbd4b23a5b7371de4f2d8d33.tar.gz
zig-ba549a7d67d0268cdbd4b23a5b7371de4f2d8d33.zip
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.
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/zig/Ast.zig18
-rw-r--r--lib/std/zig/AstGen.zig149
-rw-r--r--lib/std/zig/Zir.zig135
3 files changed, 178 insertions, 124 deletions
diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig
index 9214499c41..0405e410ee 100644
--- a/lib/std/zig/Ast.zig
+++ b/lib/std/zig/Ast.zig
@@ -2877,24 +2877,6 @@ pub const full = struct {
arrow_token: TokenIndex,
target_expr: Node.Index,
};
-
- /// Returns:
- /// `null` if case is not special
- /// `.none` if case is else prong
- /// Index of underscore otherwise
- pub fn isSpecial(case: *const SwitchCase, tree: *const Ast) ?Node.OptionalIndex {
- if (case.ast.values.len == 0) {
- return .none;
- }
- for (case.ast.values) |val| {
- if (tree.nodeTag(val) == .identifier and
- mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_"))
- {
- return val.toOptional();
- }
- }
- return null;
- }
};
pub const Asm = struct {
diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig
index fbb546411f..670dbd755d 100644
--- a/lib/std/zig/AstGen.zig
+++ b/lib/std/zig/AstGen.zig
@@ -7662,11 +7662,12 @@ fn switchExpr(
var scalar_cases_len: u32 = 0;
var multi_cases_len: u32 = 0;
var inline_cases_len: u32 = 0;
- var special_prong: Zir.SpecialProng = .none;
- var special_node: Ast.Node.OptionalIndex = .none;
+ var else_case_node: Ast.Node.OptionalIndex = .none;
var else_src: ?Ast.TokenIndex = null;
- var underscore_src: ?Ast.TokenIndex = null;
+ var underscore_case_node: Ast.Node.OptionalIndex = .none;
var underscore_node: Ast.Node.OptionalIndex = .none;
+ var underscore_src: ?Ast.TokenIndex = null;
+ var underscore_additional_items: Zir.SpecialProngs.AdditionalItems = .none;
for (case_nodes) |case_node| {
const case = tree.fullSwitchCase(case_node).?;
if (case.payload_token) |payload_token| {
@@ -7687,6 +7688,7 @@ fn switchExpr(
any_non_inline_capture = true;
}
}
+
// Check for else prong.
if (case.ast.values.len == 0) {
const case_src = case.ast.arrow_token - 1;
@@ -7703,40 +7705,21 @@ fn switchExpr(
),
},
);
- } else if (underscore_src) |some_underscore| {
- return astgen.failNodeNotes(
- node,
- "else and '_' prong in switch expression",
- .{},
- &[_]u32{
- try astgen.errNoteTok(
- case_src,
- "else prong here",
- .{},
- ),
- try astgen.errNoteTok(
- some_underscore,
- "'_' prong here",
- .{},
- ),
- },
- );
}
- special_node = case_node.toOptional();
- special_prong = .@"else";
+ else_case_node = case_node.toOptional();
else_src = case_src;
continue;
}
// Check for '_' prong.
- var found_underscore = false;
+ var case_has_underscore = false;
for (case.ast.values) |val| {
switch (tree.nodeTag(val)) {
.identifier => if (mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_")) {
- const case_src = case.ast.arrow_token - 1;
+ const val_src = tree.nodeMainToken(val);
if (underscore_src) |src| {
return astgen.failTokNotes(
- case_src,
+ val_src,
"multiple '_' prongs in switch expression",
.{},
&[_]u32{
@@ -7747,39 +7730,26 @@ fn switchExpr(
),
},
);
- } else if (else_src) |some_else| {
- return astgen.failNodeNotes(
- node,
- "else and '_' prong in switch expression",
- .{},
- &[_]u32{
- try astgen.errNoteTok(
- some_else,
- "else prong here",
- .{},
- ),
- try astgen.errNoteTok(
- case_src,
- "'_' prong here",
- .{},
- ),
- },
- );
}
if (case.inline_token != null) {
- return astgen.failTok(case_src, "cannot inline '_' prong", .{});
+ return astgen.failTok(val_src, "cannot inline '_' prong", .{});
}
- special_node = case_node.toOptional();
- special_prong = if (case.ast.values.len == 1) .under else .absorbing_under;
- underscore_src = case_src;
+ underscore_case_node = case_node.toOptional();
+ underscore_src = val_src;
underscore_node = val.toOptional();
- found_underscore = true;
+ underscore_additional_items = switch (case.ast.values.len) {
+ 0 => unreachable,
+ 1 => .none,
+ 2 => .one,
+ else => .many,
+ };
+ case_has_underscore = true;
},
.string_literal => return astgen.failNode(val, "cannot switch on strings", .{}),
else => {},
}
}
- if (found_underscore) continue;
+ if (case_has_underscore) continue;
if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) {
scalar_cases_len += 1;
@@ -7791,6 +7761,14 @@ fn switchExpr(
}
}
+ const special_prongs: Zir.SpecialProngs = .init(
+ else_src != null,
+ underscore_src != null,
+ underscore_additional_items,
+ );
+ const has_else = special_prongs.hasElse();
+ const has_under = special_prongs.hasUnder();
+
const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
astgen.advanceSourceCursorToNode(operand_node);
@@ -7811,7 +7789,9 @@ fn switchExpr(
const payloads = &astgen.scratch;
const scratch_top = astgen.scratch.items.len;
const case_table_start = scratch_top;
- const scalar_case_table = case_table_start + @intFromBool(special_prong != .none);
+ const else_case_index = if (has_else) case_table_start else undefined;
+ const under_case_index = if (has_under) case_table_start + @intFromBool(has_else) else undefined;
+ const scalar_case_table = case_table_start + @intFromBool(has_else) + @intFromBool(has_under);
const multi_case_table = scalar_case_table + scalar_cases_len;
const case_table_end = multi_case_table + multi_cases_len;
try astgen.scratch.resize(gpa, case_table_end);
@@ -7943,9 +7923,19 @@ fn switchExpr(
const header_index: u32 = @intCast(payloads.items.len);
const body_len_index = if (is_multi_case) blk: {
- if (case_node.toOptional() == special_node) {
- assert(special_prong == .absorbing_under);
- payloads.items[case_table_start] = header_index;
+ if (case_node.toOptional() == underscore_case_node) {
+ payloads.items[under_case_index] = header_index;
+ if (special_prongs.hasOneAdditionalItem()) {
+ try payloads.resize(gpa, header_index + 2); // item, body_len
+ const maybe_item_node = case.ast.values[0];
+ const item_node = if (maybe_item_node.toOptional() == underscore_node)
+ case.ast.values[1]
+ else
+ maybe_item_node;
+ const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
+ payloads.items[header_index] = @intFromEnum(item_inst);
+ break :blk header_index + 1;
+ }
} else {
payloads.items[multi_case_table + multi_case_index] = header_index;
multi_case_index += 1;
@@ -7985,9 +7975,13 @@ fn switchExpr(
payloads.items[header_index] = items_len;
payloads.items[header_index + 1] = ranges_len;
break :blk header_index + 2;
- } else if (case_node.toOptional() == special_node) blk: {
- assert(special_prong != .absorbing_under);
- payloads.items[case_table_start] = header_index;
+ } else if (case_node.toOptional() == else_case_node) blk: {
+ payloads.items[else_case_index] = header_index;
+ try payloads.resize(gpa, header_index + 1); // body_len
+ break :blk header_index;
+ } else if (case_node.toOptional() == underscore_case_node) blk: {
+ assert(!special_prongs.hasAdditionalItems());
+ payloads.items[under_case_index] = header_index;
try payloads.resize(gpa, header_index + 1); // body_len
break :blk header_index;
} else blk: {
@@ -8048,7 +8042,7 @@ fn switchExpr(
.operand = raw_operand,
.bits = Zir.Inst.SwitchBlock.Bits{
.has_multi_cases = multi_cases_len != 0,
- .special_prong = special_prong,
+ .special_prongs = special_prongs,
.any_has_tag_capture = any_has_tag_capture,
.any_non_inline_capture = any_non_inline_capture,
.has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue,
@@ -8067,29 +8061,40 @@ fn switchExpr(
const zir_datas = astgen.instructions.items(.data);
zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
- var normal_case_table_start = case_table_start;
- if (special_prong != .none) {
- normal_case_table_start += 1;
-
- const start_index = payloads.items[case_table_start];
+ if (has_else) {
+ const start_index = payloads.items[else_case_index];
+ var end_index = start_index + 1;
+ const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[start_index]);
+ end_index += prong_info.body_len;
+ astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
+ }
+ if (has_under) {
+ const start_index = payloads.items[under_case_index];
var body_len_index = start_index;
var end_index = start_index;
- if (special_prong == .absorbing_under) {
- body_len_index += 2;
- const items_len = payloads.items[start_index];
- const ranges_len = payloads.items[start_index + 1];
- end_index += 3 + items_len + 2 * ranges_len;
- } else {
- end_index += 1;
+ switch (underscore_additional_items) {
+ .none => {
+ end_index += 1;
+ },
+ .one => {
+ body_len_index += 1;
+ end_index += 2;
+ },
+ .many => {
+ body_len_index += 2;
+ const items_len = payloads.items[start_index];
+ const ranges_len = payloads.items[start_index + 1];
+ end_index += 3 + items_len + 2 * ranges_len;
+ },
}
const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
end_index += prong_info.body_len;
astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
}
- for (payloads.items[normal_case_table_start..case_table_end], 0..) |start_index, i| {
+ for (payloads.items[scalar_case_table..case_table_end], 0..) |start_index, i| {
var body_len_index = start_index;
var end_index = start_index;
- const table_index = normal_case_table_start + i;
+ const table_index = scalar_case_table + i;
if (table_index < multi_case_table) {
body_len_index += 1;
end_index += 2;
diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig
index dca845d622..f6781a74a2 100644
--- a/lib/std/zig/Zir.zig
+++ b/lib/std/zig/Zir.zig
@@ -3226,9 +3226,14 @@ pub const Inst = struct {
/// 0. multi_cases_len: u32 // If has_multi_cases is set.
/// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture.
- /// 2. else_body { // If special_prong != .none
- /// items_len: u32, // If special_prong == .absorbing_under
- /// ranges_len: u32, // If special_prong == .absorbing_under
+ /// 2. else_body { // If special_prong.hasElse() is set.
+ /// info: ProngInfo,
+ /// body member Index for every info.body_len
+ /// }
+ /// 3. under_body { // If special_prong.hasUnder() is set.
+ /// item: Ref, // If special_prong.hasOneAdditionalItem() is set.
+ /// items_len: u32, // If special_prong.hasManyAdditionalItems() is set.
+ /// ranges_len: u32, // If special_prong.hasManyAdditionalItems() is set.
/// info: ProngInfo,
/// item: Ref, // for every items_len
/// ranges: { // for every ranges_len
@@ -3237,12 +3242,12 @@ pub const Inst = struct {
/// }
/// body member Index for every info.body_len
/// }
- /// 3. scalar_cases: { // for every scalar_cases_len
+ /// 4. scalar_cases: { // for every scalar_cases_len
/// item: Ref,
/// info: ProngInfo,
/// body member Index for every info.body_len
/// }
- /// 4. multi_cases: { // for every multi_cases_len
+ /// 5. multi_cases: { // for every multi_cases_len
/// items_len: u32,
/// ranges_len: u32,
/// info: ProngInfo,
@@ -3283,16 +3288,17 @@ pub const Inst = struct {
/// If true, one or more prongs have multiple items.
has_multi_cases: bool,
/// Information about the special prong.
- special_prong: SpecialProng,
+ special_prongs: SpecialProngs,
/// If true, at least one prong has an inline tag capture.
any_has_tag_capture: bool,
/// If true, at least one prong has a capture which may not
/// be comptime-known via `inline`.
any_non_inline_capture: bool,
+ /// If true, at least one prong contains a `continue`.
has_continue: bool,
scalar_cases_len: ScalarCasesLen,
- pub const ScalarCasesLen = u26;
+ pub const ScalarCasesLen = u25;
};
pub const MultiProng = struct {
@@ -3868,17 +3874,67 @@ pub const Inst = struct {
};
};
-pub const SpecialProng = enum(u2) {
- none,
- /// Simple else prong.
- /// `else => {}`
- @"else",
- /// Simple '_' prong.
- /// `_ => {}`
- under,
- /// '_' prong with additional items.
- /// `a, _, b => {}`
- absorbing_under,
+pub const SpecialProngs = enum(u3) {
+ none = 0b000,
+ /// Simple `else` prong.
+ /// `else => {},`
+ @"else" = 0b001,
+ /// Simple `_` prong.
+ /// `_ => {},`
+ under = 0b010,
+ /// Both an `else` and a `_` prong.
+ /// `else => {},`
+ /// `_ => {},`
+ under_and_else = 0b011,
+ /// `_` prong with 1 additional item.
+ /// `a, _ => {},`
+ under_one_item = 0b100,
+ /// Both an `else` and a `_` prong with 1 additional item.
+ /// `else => {},`
+ /// `a, _ => {},`
+ under_one_item_and_else = 0b101,
+ /// `_` prong with >1 additional items.
+ /// `a, _, b => {},`
+ under_many_items = 0b110,
+ /// Both an `else` and a `_` prong with >1 additional items.
+ /// `else => {},`
+ /// `a, _, b => {},`
+ under_many_items_and_else = 0b111,
+
+ pub const AdditionalItems = enum(u3) {
+ none = @intFromEnum(SpecialProngs.under),
+ one = @intFromEnum(SpecialProngs.under_one_item),
+ many = @intFromEnum(SpecialProngs.under_many_items),
+ };
+
+ pub fn init(has_else: bool, has_under: bool, additional_items: AdditionalItems) SpecialProngs {
+ const else_bit: u3 = @intFromBool(has_else);
+ const under_bits: u3 = if (has_under)
+ @intFromEnum(additional_items)
+ else
+ @intFromEnum(SpecialProngs.none);
+ return @enumFromInt(else_bit | under_bits);
+ }
+
+ pub fn hasElse(special_prongs: SpecialProngs) bool {
+ return (@intFromEnum(special_prongs) & 0b001) != 0;
+ }
+
+ pub fn hasUnder(special_prongs: SpecialProngs) bool {
+ return (@intFromEnum(special_prongs) & 0b110) != 0;
+ }
+
+ pub fn hasAdditionalItems(special_prongs: SpecialProngs) bool {
+ return (@intFromEnum(special_prongs) & 0b100) != 0;
+ }
+
+ pub fn hasOneAdditionalItem(special_prongs: SpecialProngs) bool {
+ return (@intFromEnum(special_prongs) & 0b110) == @intFromEnum(SpecialProngs.under_one_item);
+ }
+
+ pub fn hasManyAdditionalItems(special_prongs: SpecialProngs) bool {
+ return (@intFromEnum(special_prongs) & 0b110) == @intFromEnum(SpecialProngs.under_many_items);
+ }
};
pub const DeclIterator = struct {
@@ -4723,7 +4779,7 @@ fn findTrackableSwitch(
}
const has_special = switch (kind) {
- .normal => extra.data.bits.special_prong != .none,
+ .normal => extra.data.bits.special_prongs != .none,
.err_union => has_special: {
// Handle `non_err_body` first.
const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
@@ -4738,29 +4794,40 @@ fn findTrackableSwitch(
};
if (has_special) {
- if (kind == .normal) {
- if (extra.data.bits.special_prong == .absorbing_under) {
- const items_len = zir.extra[extra_index];
- extra_index += 1;
- const ranges_len = zir.extra[extra_index];
- extra_index += 1;
- const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
- extra_index += 1;
+ const has_else = if (kind == .normal)
+ extra.data.bits.special_prongs.hasElse()
+ else
+ true;
+ if (has_else) {
+ const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
+ extra_index += 1;
+ const body = zir.bodySlice(extra_index, prong_info.body_len);
+ extra_index += body.len;
- extra_index += items_len + ranges_len * 2;
+ try zir.findTrackableBody(gpa, contents, defers, body);
+ }
+ if (kind == .normal) {
+ const special_prongs = extra.data.bits.special_prongs;
+ if (special_prongs.hasUnder()) {
+ var trailing_items_len: u32 = 0;
+ if (special_prongs.hasOneAdditionalItem()) {
+ extra_index += 1;
+ } else if (special_prongs.hasManyAdditionalItems()) {
+ const items_len = zir.extra[extra_index];
+ extra_index += 1;
+ const ranges_len = zir.extra[extra_index];
+ extra_index += 1;
+ trailing_items_len = items_len + ranges_len * 2;
+ }
+ const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
+ extra_index += 1 + trailing_items_len;
const body = zir.bodySlice(extra_index, prong_info.body_len);
extra_index += body.len;
try zir.findTrackableBody(gpa, contents, defers, body);
}
}
- const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
- extra_index += 1;
- const body = zir.bodySlice(extra_index, prong_info.body_len);
- extra_index += body.len;
-
- try zir.findTrackableBody(gpa, contents, defers, body);
}
{