diff options
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/zig/AstGen.zig | 207 | ||||
| -rw-r--r-- | lib/std/zig/Zir.zig | 143 |
2 files changed, 238 insertions, 112 deletions
diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 29f39cb6a2..99c80816ad 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -7662,10 +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_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| { @@ -7686,7 +7688,8 @@ fn switchExpr( any_non_inline_capture = true; } } - // Check for else/`_` prong. + + // Check for else prong. if (case.ast.values.len == 0) { const case_src = case.ast.arrow_token - 1; if (else_src) |src| { @@ -7702,79 +7705,51 @@ 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; - } else if (case.ast.values.len == 1 and - tree.nodeTag(case.ast.values[0]) == .identifier and - mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_")) - { - const case_src = case.ast.arrow_token - 1; - if (underscore_src) |src| { - return astgen.failTokNotes( - case_src, - "multiple '_' prongs in switch expression", - .{}, - &[_]u32{ - try astgen.errNoteTok( - src, - "previous '_' prong here", - .{}, - ), - }, - ); - } 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", .{}); - } - special_node = case_node.toOptional(); - special_prong = .under; - underscore_src = case_src; - continue; } + // Check for '_' prong. + var case_has_underscore = false; for (case.ast.values) |val| { - if (tree.nodeTag(val) == .string_literal) - return astgen.failNode(val, "cannot switch on strings", .{}); + switch (tree.nodeTag(val)) { + .identifier => if (mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_")) { + const val_src = tree.nodeMainToken(val); + if (underscore_src) |src| { + return astgen.failTokNotes( + val_src, + "multiple '_' prongs in switch expression", + .{}, + &[_]u32{ + try astgen.errNoteTok( + src, + "previous '_' prong here", + .{}, + ), + }, + ); + } + if (case.inline_token != null) { + return astgen.failTok(val_src, "cannot inline '_' prong", .{}); + } + underscore_case_node = case_node.toOptional(); + underscore_src = val_src; + underscore_node = val.toOptional(); + 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 (case_has_underscore) continue; if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) { scalar_cases_len += 1; @@ -7786,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); @@ -7806,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); @@ -7938,14 +7923,33 @@ fn switchExpr( const header_index: u32 = @intCast(payloads.items.len); const body_len_index = if (is_multi_case) blk: { - payloads.items[multi_case_table + multi_case_index] = header_index; - multi_case_index += 1; + 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; + } try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len // items var items_len: u32 = 0; for (case.ast.values) |item_node| { - if (tree.nodeTag(item_node) == .switch_range) continue; + if (item_node.toOptional() == underscore_node or + tree.nodeTag(item_node) == .switch_range) + { + continue; + } items_len += 1; const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item); @@ -7955,7 +7959,9 @@ fn switchExpr( // ranges var ranges_len: u32 = 0; for (case.ast.values) |range| { - if (tree.nodeTag(range) != .switch_range) continue; + if (tree.nodeTag(range) != .switch_range) { + continue; + } ranges_len += 1; const first_node, const last_node = tree.nodeData(range).node_and_node; @@ -7969,8 +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: { - 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: { @@ -8025,15 +8036,13 @@ fn switchExpr( try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len + @intFromBool(multi_cases_len != 0) + @intFromBool(any_has_tag_capture) + - payloads.items.len - case_table_end + - (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len); + payloads.items.len - scratch_top); const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{ .operand = raw_operand, .bits = Zir.Inst.SwitchBlock.Bits{ .has_multi_cases = multi_cases_len != 0, - .has_else = special_prong == .@"else", - .has_under = special_prong == .under, + .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, @@ -8052,13 +8061,41 @@ fn switchExpr( const zir_datas = astgen.instructions.items(.data); zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index; - for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| { + 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; - const table_index = case_table_start + i; - if (table_index < scalar_case_table) { - end_index += 1; - } else if (table_index < multi_case_table) { + 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[scalar_case_table..case_table_end], 0..) |start_index, i| { + var body_len_index = start_index; + var end_index = start_index; + const table_index = scalar_case_table + i; + if (table_index < multi_case_table) { body_len_index += 1; end_index += 2; } else { diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 1bb0b9d37f..f6781a74a2 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -3226,20 +3226,32 @@ 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 has_else or has_under is set. + /// 2. else_body { // If special_prong.hasElse() is set. /// info: ProngInfo, /// body member Index for every info.body_len /// } - /// 3. scalar_cases: { // for every scalar_cases_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 + /// item_first: Ref, + /// item_last: Ref, + /// } + /// body member Index for every info.body_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, - /// item: Ref // for every items_len + /// item: Ref, // for every items_len /// ranges: { // for every ranges_len /// item_first: Ref, /// item_last: Ref, @@ -3275,30 +3287,18 @@ pub const Inst = struct { pub const Bits = packed struct(u32) { /// If true, one or more prongs have multiple items. has_multi_cases: bool, - /// If true, there is an else prong. This is mutually exclusive with `has_under`. - has_else: bool, - /// If true, there is an underscore prong. This is mutually exclusive with `has_else`. - has_under: bool, + /// Information about the special prong. + 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 fn specialProng(bits: Bits) SpecialProng { - const has_else: u2 = @intFromBool(bits.has_else); - const has_under: u2 = @intFromBool(bits.has_under); - return switch ((has_else << 1) | has_under) { - 0b00 => .none, - 0b01 => .under, - 0b10 => .@"else", - 0b11 => unreachable, - }; - } + pub const ScalarCasesLen = u25; }; pub const MultiProng = struct { @@ -3874,7 +3874,68 @@ pub const Inst = struct { }; }; -pub const SpecialProng = enum { none, @"else", 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 { extra_index: u32, @@ -4718,7 +4779,7 @@ fn findTrackableSwitch( } const has_special = switch (kind) { - .normal => extra.data.bits.specialProng() != .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]); @@ -4733,12 +4794,40 @@ fn findTrackableSwitch( }; if (has_special) { - 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; + 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; - try zir.findTrackableBody(gpa, contents, defers, body); + 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); + } + } } { |
