diff options
| author | Justus Klausecker <justus@klausecker.de> | 2025-07-10 01:58:02 +0200 |
|---|---|---|
| committer | Justus Klausecker <justus@klausecker.de> | 2025-08-07 13:58:47 +0200 |
| commit | ba549a7d67d0268cdbd4b23a5b7371de4f2d8d33 (patch) | |
| tree | 2ee5ed3a9546923fa41abeac193634f88f944d82 /lib/std | |
| parent | 1d9b1c021273037efea6623e669800ad538c8075 (diff) | |
| download | zig-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.zig | 18 | ||||
| -rw-r--r-- | lib/std/zig/AstGen.zig | 149 | ||||
| -rw-r--r-- | lib/std/zig/Zir.zig | 135 |
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); } { |
