mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
Merge pull request #24381 from Justus2308/switch-better-underscore
Enhance switch on non-exhaustive enums
This commit is contained in:
commit
6e90ce2536
12 changed files with 925 additions and 322 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
|||
566
src/Sema.zig
566
src/Sema.zig
|
|
@ -10927,7 +10927,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
|
|||
const switch_src = block.nodeOffset(inst_data.src_node);
|
||||
const switch_src_node_offset = inst_data.src_node;
|
||||
const switch_operand_src = block.src(.{ .node_offset_switch_operand = switch_src_node_offset });
|
||||
const else_prong_src = block.src(.{ .node_offset_switch_special_prong = switch_src_node_offset });
|
||||
const else_prong_src = block.src(.{ .node_offset_switch_else_prong = switch_src_node_offset });
|
||||
const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);
|
||||
const main_operand_src = block.src(.{ .node_offset_if_cond = extra.data.main_src_node_offset });
|
||||
const main_src = block.src(.{ .node_offset_main_token = extra.data.main_src_node_offset });
|
||||
|
|
@ -11121,6 +11121,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
|
|||
err_val,
|
||||
operand_err_set_ty,
|
||||
switch_src_node_offset,
|
||||
null,
|
||||
.{
|
||||
.body = else_case.body,
|
||||
.end = else_case.end,
|
||||
|
|
@ -11128,6 +11129,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
|
|||
.is_inline = else_case.is_inline,
|
||||
.has_tag_capture = false,
|
||||
},
|
||||
false,
|
||||
case_vals,
|
||||
scalar_cases_len,
|
||||
multi_cases_len,
|
||||
|
|
@ -11199,6 +11201,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
|
|||
true,
|
||||
switch_src_node_offset,
|
||||
else_prong_src,
|
||||
false,
|
||||
undefined,
|
||||
seen_errors,
|
||||
undefined,
|
||||
|
|
@ -11206,6 +11209,10 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
|
|||
undefined,
|
||||
cond_dbg_node_index,
|
||||
true,
|
||||
null,
|
||||
undefined,
|
||||
&.{},
|
||||
&.{},
|
||||
);
|
||||
|
||||
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
|
||||
|
|
@ -11242,12 +11249,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
const ip = &zcu.intern_pool;
|
||||
const gpa = sema.gpa;
|
||||
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
|
||||
const src = block.nodeOffset(inst_data.src_node);
|
||||
const src_node_offset = inst_data.src_node;
|
||||
const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
|
||||
const special_prong_src = block.src(.{ .node_offset_switch_special_prong = src_node_offset });
|
||||
const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
|
||||
const under_prong_src = block.src(.{ .node_offset_switch_under_prong = src_node_offset });
|
||||
const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
|
||||
|
||||
const operand: SwitchProngAnalysis.Operand, const raw_operand_ty: Type = op: {
|
||||
|
|
@ -11334,27 +11343,63 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
|
||||
defer case_vals.deinit(gpa);
|
||||
|
||||
const special_prong = extra.data.bits.specialProng();
|
||||
const special: SpecialProng = switch (special_prong) {
|
||||
.none => .{
|
||||
.body = &.{},
|
||||
.end = header_extra_index,
|
||||
.capture = .none,
|
||||
.is_inline = false,
|
||||
.has_tag_capture = false,
|
||||
},
|
||||
.under, .@"else" => blk: {
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
|
||||
const extra_body_start = header_extra_index + 1;
|
||||
break :blk .{
|
||||
.body = sema.code.bodySlice(extra_body_start, info.body_len),
|
||||
.end = extra_body_start + info.body_len,
|
||||
.capture = info.capture,
|
||||
.is_inline = info.is_inline,
|
||||
.has_tag_capture = info.has_tag_capture,
|
||||
};
|
||||
},
|
||||
var single_absorbed_item: Zir.Inst.Ref = .none;
|
||||
var absorbed_items: []const Zir.Inst.Ref = &.{};
|
||||
var absorbed_ranges: []const Zir.Inst.Ref = &.{};
|
||||
|
||||
const special_prongs = extra.data.bits.special_prongs;
|
||||
const has_else = special_prongs.hasElse();
|
||||
const has_under = special_prongs.hasUnder();
|
||||
const special_else: SpecialProng = if (has_else) blk: {
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
|
||||
const extra_body_start = header_extra_index + 1;
|
||||
break :blk .{
|
||||
.body = sema.code.bodySlice(extra_body_start, info.body_len),
|
||||
.end = extra_body_start + info.body_len,
|
||||
.capture = info.capture,
|
||||
.is_inline = info.is_inline,
|
||||
.has_tag_capture = info.has_tag_capture,
|
||||
};
|
||||
} else .{
|
||||
.body = &.{},
|
||||
.end = header_extra_index,
|
||||
.capture = .none,
|
||||
.is_inline = false,
|
||||
.has_tag_capture = false,
|
||||
};
|
||||
const special_under: SpecialProng = if (has_under) blk: {
|
||||
var extra_index = special_else.end;
|
||||
var trailing_items_len: usize = 0;
|
||||
if (special_prongs.hasOneAdditionalItem()) {
|
||||
single_absorbed_item = @enumFromInt(sema.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
absorbed_items = @ptrCast(&single_absorbed_item);
|
||||
} else if (special_prongs.hasManyAdditionalItems()) {
|
||||
const items_len = sema.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const ranges_len = sema.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
absorbed_items = sema.code.refSlice(extra_index + 1, items_len);
|
||||
absorbed_ranges = sema.code.refSlice(extra_index + 1 + items_len, ranges_len * 2);
|
||||
trailing_items_len = items_len + ranges_len * 2;
|
||||
}
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
|
||||
extra_index += 1 + trailing_items_len;
|
||||
break :blk .{
|
||||
.body = sema.code.bodySlice(extra_index, info.body_len),
|
||||
.end = extra_index + info.body_len,
|
||||
.capture = info.capture,
|
||||
.is_inline = info.is_inline,
|
||||
.has_tag_capture = info.has_tag_capture,
|
||||
};
|
||||
} else .{
|
||||
.body = &.{},
|
||||
.end = special_else.end,
|
||||
.capture = .none,
|
||||
.is_inline = false,
|
||||
.has_tag_capture = false,
|
||||
};
|
||||
const special_end = special_under.end;
|
||||
|
||||
// Duplicate checking variables later also used for `inline else`.
|
||||
var seen_enum_fields: []?LazySrcLoc = &.{};
|
||||
|
|
@ -11374,7 +11419,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
var else_error_ty: ?Type = null;
|
||||
|
||||
// Validate usage of '_' prongs.
|
||||
if (special_prong == .under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) {
|
||||
if (has_under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(
|
||||
src,
|
||||
|
|
@ -11383,7 +11428,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
);
|
||||
errdefer msg.destroy(gpa);
|
||||
try sema.errNote(
|
||||
special_prong_src,
|
||||
under_prong_src,
|
||||
msg,
|
||||
"'_' prong here",
|
||||
.{},
|
||||
|
|
@ -11408,7 +11453,23 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
@memset(seen_enum_fields, null);
|
||||
// `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
|
||||
|
||||
var extra_index: usize = special.end;
|
||||
for (absorbed_items, 0..) |item_ref, item_i| {
|
||||
_ = try sema.validateSwitchItemEnum(
|
||||
block,
|
||||
seen_enum_fields,
|
||||
&range_set,
|
||||
item_ref,
|
||||
cond_ty,
|
||||
block.src(.{ .switch_case_item = .{
|
||||
.switch_node_offset = src_node_offset,
|
||||
.case_idx = .special_under,
|
||||
.item_idx = .{ .kind = .single, .index = @intCast(item_i) },
|
||||
} }),
|
||||
);
|
||||
}
|
||||
try sema.validateSwitchNoRange(block, @intCast(absorbed_ranges.len), cond_ty, src_node_offset);
|
||||
|
||||
var extra_index: usize = special_end;
|
||||
{
|
||||
var scalar_i: u32 = 0;
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
|
|
@ -11466,13 +11527,22 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
if (seen_src == null) break false;
|
||||
} else true;
|
||||
|
||||
if (special_prong == .@"else") {
|
||||
if (all_tags_handled and !cond_ty.isNonexhaustiveEnum(zcu)) return sema.fail(
|
||||
block,
|
||||
special_prong_src,
|
||||
"unreachable else prong; all cases already handled",
|
||||
.{},
|
||||
);
|
||||
if (has_else) {
|
||||
if (all_tags_handled) {
|
||||
if (cond_ty.isNonexhaustiveEnum(zcu)) {
|
||||
if (has_under) return sema.fail(
|
||||
block,
|
||||
else_prong_src,
|
||||
"unreachable else prong; all explicit cases already handled",
|
||||
.{},
|
||||
);
|
||||
} else return sema.fail(
|
||||
block,
|
||||
else_prong_src,
|
||||
"unreachable else prong; all cases already handled",
|
||||
.{},
|
||||
);
|
||||
}
|
||||
} else if (!all_tags_handled) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(
|
||||
|
|
@ -11490,7 +11560,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
i,
|
||||
msg,
|
||||
"unhandled enumeration value: '{f}'",
|
||||
.{field_name.fmt(&zcu.intern_pool)},
|
||||
.{field_name.fmt(ip)},
|
||||
);
|
||||
}
|
||||
try sema.errNote(
|
||||
|
|
@ -11502,11 +11572,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
} else if (special_prong == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
|
||||
} else if (special_prongs == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
|
||||
return sema.fail(
|
||||
block,
|
||||
src,
|
||||
"switch on non-exhaustive enum must include 'else' or '_' prong",
|
||||
"switch on non-exhaustive enum must include 'else' or '_' prong or both",
|
||||
.{},
|
||||
);
|
||||
}
|
||||
|
|
@ -11520,11 +11590,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
inst_data,
|
||||
scalar_cases_len,
|
||||
multi_cases_len,
|
||||
.{ .body = special.body, .end = special.end, .src = special_prong_src },
|
||||
special_prong == .@"else",
|
||||
.{ .body = special_else.body, .end = special_else.end, .src = else_prong_src },
|
||||
has_else,
|
||||
),
|
||||
.int, .comptime_int => {
|
||||
var extra_index: usize = special.end;
|
||||
var extra_index: usize = special_end;
|
||||
{
|
||||
var scalar_i: u32 = 0;
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
|
|
@ -11606,10 +11676,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
const min_int = try cond_ty.minInt(pt, cond_ty);
|
||||
const max_int = try cond_ty.maxInt(pt, cond_ty);
|
||||
if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {
|
||||
if (special_prong == .@"else") {
|
||||
if (has_else) {
|
||||
return sema.fail(
|
||||
block,
|
||||
special_prong_src,
|
||||
else_prong_src,
|
||||
"unreachable else prong; all cases already handled",
|
||||
.{},
|
||||
);
|
||||
|
|
@ -11617,7 +11687,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
break :check_range;
|
||||
}
|
||||
}
|
||||
if (special_prong != .@"else") {
|
||||
if (special_prongs == .none) {
|
||||
return sema.fail(
|
||||
block,
|
||||
src,
|
||||
|
|
@ -11628,7 +11698,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
}
|
||||
},
|
||||
.bool => {
|
||||
var extra_index: usize = special.end;
|
||||
var extra_index: usize = special_end;
|
||||
{
|
||||
var scalar_i: u32 = 0;
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
|
|
@ -11680,31 +11750,28 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
|
||||
}
|
||||
}
|
||||
switch (special_prong) {
|
||||
.@"else" => {
|
||||
if (true_count + false_count == 2) {
|
||||
return sema.fail(
|
||||
block,
|
||||
special_prong_src,
|
||||
"unreachable else prong; all cases already handled",
|
||||
.{},
|
||||
);
|
||||
}
|
||||
},
|
||||
.under, .none => {
|
||||
if (true_count + false_count < 2) {
|
||||
return sema.fail(
|
||||
block,
|
||||
src,
|
||||
"switch must handle all possibilities",
|
||||
.{},
|
||||
);
|
||||
}
|
||||
},
|
||||
if (has_else) {
|
||||
if (true_count + false_count == 2) {
|
||||
return sema.fail(
|
||||
block,
|
||||
else_prong_src,
|
||||
"unreachable else prong; all cases already handled",
|
||||
.{},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (true_count + false_count < 2) {
|
||||
return sema.fail(
|
||||
block,
|
||||
src,
|
||||
"switch must handle all possibilities",
|
||||
.{},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
.enum_literal, .void, .@"fn", .pointer, .type => {
|
||||
if (special_prong != .@"else") {
|
||||
if (!has_else) {
|
||||
return sema.fail(
|
||||
block,
|
||||
src,
|
||||
|
|
@ -11716,7 +11783,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
var seen_values = ValueSrcMap{};
|
||||
defer seen_values.deinit(gpa);
|
||||
|
||||
var extra_index: usize = special.end;
|
||||
var extra_index: usize = special_end;
|
||||
{
|
||||
var scalar_i: u32 = 0;
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
|
|
@ -11789,6 +11856,16 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
}),
|
||||
}
|
||||
|
||||
var special_members_only: ?SpecialProng = null;
|
||||
var special_members_only_src: LazySrcLoc = undefined;
|
||||
const special_generic, const special_generic_src = if (has_under) b: {
|
||||
if (has_else) {
|
||||
special_members_only = special_else;
|
||||
special_members_only_src = else_prong_src;
|
||||
}
|
||||
break :b .{ special_under, under_prong_src };
|
||||
} else .{ special_else, else_prong_src };
|
||||
|
||||
const spa: SwitchProngAnalysis = .{
|
||||
.sema = sema,
|
||||
.parent_block = block,
|
||||
|
|
@ -11835,11 +11912,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
defer child_block.instructions.deinit(gpa);
|
||||
defer merges.deinit(gpa);
|
||||
|
||||
if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
|
||||
if (scalar_cases_len + multi_cases_len == 0 and
|
||||
special_members_only == null and
|
||||
!special_generic.is_inline)
|
||||
{
|
||||
if (empty_enum) {
|
||||
return .void_value;
|
||||
}
|
||||
if (special_prong == .none) {
|
||||
if (special_prongs == .none) {
|
||||
return sema.fail(block, src, "switch must handle all possibilities", .{});
|
||||
}
|
||||
const init_cond = switch (operand) {
|
||||
|
|
@ -11853,7 +11933,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
const ok = try block.addUnOp(.is_named_enum_value, init_cond);
|
||||
try sema.addSafetyCheck(block, src, ok, .corrupt_switch);
|
||||
}
|
||||
if (err_set and try sema.maybeErrorUnwrap(block, special.body, init_cond, operand_src, false)) {
|
||||
if (err_set and try sema.maybeErrorUnwrap(block, special_generic.body, init_cond, operand_src, false)) {
|
||||
return .unreachable_value;
|
||||
}
|
||||
}
|
||||
|
|
@ -11873,7 +11953,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
cond_ty,
|
||||
cond_val,
|
||||
src_node_offset,
|
||||
special,
|
||||
special_members_only,
|
||||
special_generic,
|
||||
has_under,
|
||||
case_vals,
|
||||
scalar_cases_len,
|
||||
multi_cases_len,
|
||||
|
|
@ -11883,15 +11965,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
);
|
||||
}
|
||||
|
||||
if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline and !extra.data.bits.has_continue) {
|
||||
if (scalar_cases_len + multi_cases_len == 0 and
|
||||
special_members_only == null and
|
||||
!special_generic.is_inline and
|
||||
!extra.data.bits.has_continue)
|
||||
{
|
||||
return spa.resolveProngComptime(
|
||||
&child_block,
|
||||
.special,
|
||||
special.body,
|
||||
special.capture,
|
||||
special_generic.body,
|
||||
special_generic.capture,
|
||||
block.src(.{ .switch_capture = .{
|
||||
.switch_node_offset = src_node_offset,
|
||||
.case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
|
||||
.case_idx = if (has_under) .special_under else .special_else,
|
||||
} }),
|
||||
undefined, // case_vals may be undefined for special prongs
|
||||
.none,
|
||||
|
|
@ -11907,6 +11993,87 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
unreachable;
|
||||
}
|
||||
|
||||
var extra_case_vals: struct {
|
||||
items: std.ArrayListUnmanaged(Air.Inst.Ref),
|
||||
ranges: std.ArrayListUnmanaged([2]Air.Inst.Ref),
|
||||
} = .{ .items = .empty, .ranges = .empty };
|
||||
defer {
|
||||
extra_case_vals.items.deinit(gpa);
|
||||
extra_case_vals.ranges.deinit(gpa);
|
||||
}
|
||||
|
||||
// Runtime switch, if we have a special_members_only prong we need to unroll
|
||||
// it to a prong with explicit items.
|
||||
// Although this is potentially the same as `inline else` it does not count
|
||||
// towards the backward branch quota because it's an implementation detail.
|
||||
if (special_members_only != null) gen: {
|
||||
assert(cond_ty.isNonexhaustiveEnum(zcu));
|
||||
|
||||
var min_i: usize = math.maxInt(usize);
|
||||
var max_i: usize = 0;
|
||||
var seen_field_count: usize = 0;
|
||||
for (seen_enum_fields, 0..) |seen, enum_i| {
|
||||
if (seen != null) {
|
||||
seen_field_count += 1;
|
||||
} else {
|
||||
min_i = @min(min_i, enum_i);
|
||||
max_i = @max(max_i, enum_i);
|
||||
}
|
||||
}
|
||||
if (min_i == max_i) {
|
||||
seen_enum_fields[min_i] = special_members_only_src;
|
||||
const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
|
||||
const item_ref = Air.internedToRef(item_val.toIntern());
|
||||
try extra_case_vals.items.append(gpa, item_ref);
|
||||
break :gen;
|
||||
}
|
||||
const missing_field_count = seen_enum_fields.len - seen_field_count;
|
||||
|
||||
extra_case_vals.items = try .initCapacity(gpa, missing_field_count / 2);
|
||||
extra_case_vals.ranges = try .initCapacity(gpa, missing_field_count / 4);
|
||||
const int_ty = cond_ty.intTagType(zcu);
|
||||
|
||||
var last_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
|
||||
var first_ref = Air.internedToRef(last_val.toIntern());
|
||||
seen_enum_fields[min_i] = special_members_only_src;
|
||||
for (seen_enum_fields[(min_i + 1)..(max_i + 1)], (min_i + 1)..) |seen, enum_i| {
|
||||
if (seen != null) continue;
|
||||
seen_enum_fields[enum_i] = special_members_only_src;
|
||||
|
||||
const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(enum_i));
|
||||
const item_ref = Air.internedToRef(item_val.toIntern());
|
||||
|
||||
const is_next = is_next: {
|
||||
const prev_int = ip.indexToKey(last_val.toIntern()).enum_tag.int;
|
||||
|
||||
const result = try arith.incrementDefinedInt(sema, int_ty, .fromInterned(prev_int));
|
||||
if (result.overflow) break :is_next false;
|
||||
|
||||
const item_int = ip.indexToKey(item_val.toIntern()).enum_tag.int;
|
||||
break :is_next try sema.valuesEqual(.fromInterned(item_int), result.val, int_ty);
|
||||
};
|
||||
|
||||
if (is_next) {
|
||||
last_val = item_val;
|
||||
} else {
|
||||
const last_ref = Air.internedToRef(last_val.toIntern());
|
||||
if (first_ref == last_ref) {
|
||||
try extra_case_vals.items.append(gpa, first_ref);
|
||||
} else {
|
||||
try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
|
||||
}
|
||||
first_ref = item_ref;
|
||||
last_val = item_val;
|
||||
}
|
||||
}
|
||||
const last_ref = Air.internedToRef(last_val.toIntern());
|
||||
if (first_ref == last_ref) {
|
||||
try extra_case_vals.items.append(gpa, first_ref);
|
||||
} else {
|
||||
try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
|
||||
}
|
||||
}
|
||||
|
||||
const air_switch_ref = try sema.analyzeSwitchRuntimeBlock(
|
||||
spa,
|
||||
&child_block,
|
||||
|
|
@ -11918,14 +12085,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
cond_ty,
|
||||
operand_src,
|
||||
case_vals,
|
||||
special,
|
||||
special_generic,
|
||||
scalar_cases_len,
|
||||
multi_cases_len,
|
||||
union_originally,
|
||||
raw_operand_ty,
|
||||
err_set,
|
||||
src_node_offset,
|
||||
special_prong_src,
|
||||
special_generic_src,
|
||||
has_under,
|
||||
seen_enum_fields,
|
||||
seen_errors,
|
||||
range_set,
|
||||
|
|
@ -11933,6 +12101,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
|||
false_count,
|
||||
cond_dbg_node_index,
|
||||
false,
|
||||
special_members_only,
|
||||
special_members_only_src,
|
||||
extra_case_vals.items.items,
|
||||
extra_case_vals.ranges.items,
|
||||
);
|
||||
|
||||
for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {
|
||||
|
|
@ -12016,14 +12188,15 @@ fn analyzeSwitchRuntimeBlock(
|
|||
operand_ty: Type,
|
||||
operand_src: LazySrcLoc,
|
||||
case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
|
||||
special: SpecialProng,
|
||||
else_prong: SpecialProng,
|
||||
scalar_cases_len: usize,
|
||||
multi_cases_len: usize,
|
||||
union_originally: bool,
|
||||
maybe_union_ty: Type,
|
||||
err_set: bool,
|
||||
switch_node_offset: std.zig.Ast.Node.Offset,
|
||||
special_prong_src: LazySrcLoc,
|
||||
else_prong_src: LazySrcLoc,
|
||||
else_prong_is_underscore: bool,
|
||||
seen_enum_fields: []?LazySrcLoc,
|
||||
seen_errors: SwitchErrorSet,
|
||||
range_set: RangeSet,
|
||||
|
|
@ -12031,6 +12204,11 @@ fn analyzeSwitchRuntimeBlock(
|
|||
false_count: u8,
|
||||
cond_dbg_node_index: Zir.Inst.Index,
|
||||
allow_err_code_unwrap: bool,
|
||||
extra_prong: ?SpecialProng,
|
||||
/// May be `undefined` if `extra_prong` is `null`
|
||||
extra_prong_src: LazySrcLoc,
|
||||
extra_prong_items: []const Air.Inst.Ref,
|
||||
extra_prong_ranges: []const [2]Air.Inst.Ref,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
|
|
@ -12054,7 +12232,7 @@ fn analyzeSwitchRuntimeBlock(
|
|||
case_block.need_debug_scope = null; // this body is emitted regardless
|
||||
defer case_block.instructions.deinit(gpa);
|
||||
|
||||
var extra_index: usize = special.end;
|
||||
var extra_index: usize = else_prong.end;
|
||||
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
|
|
@ -12116,23 +12294,42 @@ fn analyzeSwitchRuntimeBlock(
|
|||
|
||||
var cases_len = scalar_cases_len;
|
||||
var case_val_idx: usize = scalar_cases_len;
|
||||
const multi_cases_len_with_extra_prong = multi_cases_len + @intFromBool(extra_prong != null);
|
||||
var multi_i: u32 = 0;
|
||||
while (multi_i < multi_cases_len) : (multi_i += 1) {
|
||||
const items_len = sema.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const ranges_len = sema.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
|
||||
extra_index += 1 + items_len + 2 * ranges_len;
|
||||
while (multi_i < multi_cases_len_with_extra_prong) : (multi_i += 1) {
|
||||
const is_extra_prong = multi_i == multi_cases_len;
|
||||
var items: []const Air.Inst.Ref = undefined;
|
||||
var info: Zir.Inst.SwitchBlock.ProngInfo = undefined;
|
||||
var ranges: []const [2]Air.Inst.Ref = undefined;
|
||||
var body: []const Zir.Inst.Index = undefined;
|
||||
if (is_extra_prong) {
|
||||
const prong = extra_prong.?;
|
||||
items = extra_prong_items;
|
||||
ranges = extra_prong_ranges;
|
||||
body = prong.body;
|
||||
info = .{
|
||||
.body_len = undefined,
|
||||
.capture = prong.capture,
|
||||
.is_inline = prong.is_inline,
|
||||
.has_tag_capture = prong.has_tag_capture,
|
||||
};
|
||||
} else {
|
||||
@branchHint(.likely);
|
||||
const items_len = sema.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const ranges_len = sema.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
info = @bitCast(sema.code.extra[extra_index]);
|
||||
extra_index += 1 + items_len + ranges_len * 2;
|
||||
|
||||
const items = case_vals.items[case_val_idx..][0..items_len];
|
||||
case_val_idx += items_len;
|
||||
// TODO: @ptrCast slice once Sema supports it
|
||||
const ranges: []const [2]Air.Inst.Ref = @as([*]const [2]Air.Inst.Ref, @ptrCast(case_vals.items[case_val_idx..]))[0..ranges_len];
|
||||
case_val_idx += ranges_len * 2;
|
||||
items = case_vals.items[case_val_idx..][0..items_len];
|
||||
case_val_idx += items_len;
|
||||
ranges = @ptrCast(case_vals.items[case_val_idx..][0 .. ranges_len * 2]);
|
||||
case_val_idx += ranges_len * 2;
|
||||
|
||||
const body = sema.code.bodySlice(extra_index, info.body_len);
|
||||
extra_index += info.body_len;
|
||||
body = sema.code.bodySlice(extra_index, info.body_len);
|
||||
extra_index += info.body_len;
|
||||
}
|
||||
|
||||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
case_block.error_return_trace_index = child_block.error_return_trace_index;
|
||||
|
|
@ -12142,14 +12339,29 @@ fn analyzeSwitchRuntimeBlock(
|
|||
var emit_bb = false;
|
||||
|
||||
for (ranges, 0..) |range_items, range_i| {
|
||||
var item = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable;
|
||||
const item_last = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable;
|
||||
var item = sema.resolveConstDefinedValue(block, .unneeded, range_items[0], undefined) catch unreachable;
|
||||
const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_items[1], undefined) catch unreachable;
|
||||
|
||||
while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
|
||||
// Previous validation has resolved any possible lazy values.
|
||||
const result = try arith.incrementDefinedInt(sema, operand_ty, item);
|
||||
const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
|
||||
.int => .{ item, operand_ty },
|
||||
.@"enum" => b: {
|
||||
const int_val = Value.fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int);
|
||||
break :b .{ int_val, int_val.typeOf(zcu) };
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
const result = try arith.incrementDefinedInt(sema, int_ty, int_val);
|
||||
assert(!result.overflow);
|
||||
item = result.val;
|
||||
item = switch (operand_ty.zigTypeTag(zcu)) {
|
||||
.int => result.val,
|
||||
.@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{
|
||||
.ty = operand_ty.toIntern(),
|
||||
.int = result.val.toIntern(),
|
||||
} })),
|
||||
else => unreachable,
|
||||
};
|
||||
}) {
|
||||
cases_len += 1;
|
||||
|
||||
|
|
@ -12158,11 +12370,14 @@ fn analyzeSwitchRuntimeBlock(
|
|||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
case_block.error_return_trace_index = child_block.error_return_trace_index;
|
||||
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, block.src(.{ .switch_case_item = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
|
||||
.item_idx = .{ .kind = .range, .index = @intCast(range_i) },
|
||||
} }));
|
||||
if (emit_bb) {
|
||||
const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
|
||||
.item_idx = .{ .kind = .range, .index = @intCast(range_i) },
|
||||
} });
|
||||
try sema.emitBackwardBranch(block, bb_src);
|
||||
}
|
||||
emit_bb = true;
|
||||
|
||||
const prong_hint = try spa.analyzeProngRuntime(
|
||||
|
|
@ -12207,11 +12422,14 @@ fn analyzeSwitchRuntimeBlock(
|
|||
break :blk field_ty.zigTypeTag(zcu) != .noreturn;
|
||||
} else true;
|
||||
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, block.src(.{ .switch_case_item = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
|
||||
.item_idx = .{ .kind = .single, .index = @intCast(item_i) },
|
||||
} }));
|
||||
if (emit_bb) {
|
||||
const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
|
||||
.item_idx = .{ .kind = .single, .index = @intCast(item_i) },
|
||||
} });
|
||||
try sema.emitBackwardBranch(block, bb_src);
|
||||
}
|
||||
emit_bb = true;
|
||||
|
||||
const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
|
||||
|
|
@ -12287,11 +12505,11 @@ fn analyzeSwitchRuntimeBlock(
|
|||
try branch_hints.append(gpa, prong_hint);
|
||||
|
||||
try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
|
||||
items.len + 2 * ranges_len +
|
||||
items.len + ranges.len * 2 +
|
||||
case_block.instructions.items.len);
|
||||
cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
|
||||
.items_len = @intCast(items.len),
|
||||
.ranges_len = @intCast(ranges_len),
|
||||
.ranges_len = @intCast(ranges.len),
|
||||
.body_len = @intCast(case_block.instructions.items.len),
|
||||
}));
|
||||
|
||||
|
|
@ -12308,12 +12526,14 @@ fn analyzeSwitchRuntimeBlock(
|
|||
cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
|
||||
}
|
||||
|
||||
const else_body: []const Air.Inst.Index = if (special.body.len != 0 or case_block.wantSafety()) else_body: {
|
||||
const else_body: []const Air.Inst.Index = if (else_prong.body.len != 0 or case_block.wantSafety()) else_body: {
|
||||
var emit_bb = false;
|
||||
if (special.is_inline) switch (operand_ty.zigTypeTag(zcu)) {
|
||||
// If this is true we must have a 'true' else prong and not an underscore because
|
||||
// underscore prongs can never be inlined. We've already checked for this.
|
||||
if (else_prong.is_inline) switch (operand_ty.zigTypeTag(zcu)) {
|
||||
.@"enum" => {
|
||||
if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
|
||||
return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
|
||||
return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
|
||||
operand_ty.fmt(pt),
|
||||
});
|
||||
}
|
||||
|
|
@ -12332,22 +12552,22 @@ fn analyzeSwitchRuntimeBlock(
|
|||
break :blk field_ty.zigTypeTag(zcu) != .noreturn;
|
||||
} else true;
|
||||
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
|
||||
emit_bb = true;
|
||||
|
||||
const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
|
||||
break :h try spa.analyzeProngRuntime(
|
||||
&case_block,
|
||||
.special,
|
||||
special.body,
|
||||
special.capture,
|
||||
else_prong.body,
|
||||
else_prong.capture,
|
||||
child_block.src(.{ .switch_capture = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
|
||||
.case_idx = .special_else,
|
||||
} }),
|
||||
&.{item_ref},
|
||||
item_ref,
|
||||
special.has_tag_capture,
|
||||
else_prong.has_tag_capture,
|
||||
);
|
||||
} else h: {
|
||||
_ = try case_block.addNoOp(.unreach);
|
||||
|
|
@ -12369,7 +12589,7 @@ fn analyzeSwitchRuntimeBlock(
|
|||
},
|
||||
.error_set => {
|
||||
if (operand_ty.isAnyError(zcu)) {
|
||||
return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
|
||||
return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
|
||||
operand_ty.fmt(pt),
|
||||
});
|
||||
}
|
||||
|
|
@ -12388,21 +12608,21 @@ fn analyzeSwitchRuntimeBlock(
|
|||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
case_block.error_return_trace_index = child_block.error_return_trace_index;
|
||||
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
|
||||
emit_bb = true;
|
||||
|
||||
const prong_hint = try spa.analyzeProngRuntime(
|
||||
&case_block,
|
||||
.special,
|
||||
special.body,
|
||||
special.capture,
|
||||
else_prong.body,
|
||||
else_prong.capture,
|
||||
child_block.src(.{ .switch_capture = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
|
||||
.case_idx = .special_else,
|
||||
} }),
|
||||
&.{item_ref},
|
||||
item_ref,
|
||||
special.has_tag_capture,
|
||||
else_prong.has_tag_capture,
|
||||
);
|
||||
try branch_hints.append(gpa, prong_hint);
|
||||
|
||||
|
|
@ -12428,21 +12648,21 @@ fn analyzeSwitchRuntimeBlock(
|
|||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
case_block.error_return_trace_index = child_block.error_return_trace_index;
|
||||
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
|
||||
emit_bb = true;
|
||||
|
||||
const prong_hint = try spa.analyzeProngRuntime(
|
||||
&case_block,
|
||||
.special,
|
||||
special.body,
|
||||
special.capture,
|
||||
else_prong.body,
|
||||
else_prong.capture,
|
||||
child_block.src(.{ .switch_capture = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
|
||||
.case_idx = .special_else,
|
||||
} }),
|
||||
&.{item_ref},
|
||||
item_ref,
|
||||
special.has_tag_capture,
|
||||
else_prong.has_tag_capture,
|
||||
);
|
||||
try branch_hints.append(gpa, prong_hint);
|
||||
|
||||
|
|
@ -12465,21 +12685,21 @@ fn analyzeSwitchRuntimeBlock(
|
|||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
case_block.error_return_trace_index = child_block.error_return_trace_index;
|
||||
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
|
||||
emit_bb = true;
|
||||
|
||||
const prong_hint = try spa.analyzeProngRuntime(
|
||||
&case_block,
|
||||
.special,
|
||||
special.body,
|
||||
special.capture,
|
||||
else_prong.body,
|
||||
else_prong.capture,
|
||||
child_block.src(.{ .switch_capture = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
|
||||
.case_idx = .special_else,
|
||||
} }),
|
||||
&.{.bool_true},
|
||||
.bool_true,
|
||||
special.has_tag_capture,
|
||||
else_prong.has_tag_capture,
|
||||
);
|
||||
try branch_hints.append(gpa, prong_hint);
|
||||
|
||||
|
|
@ -12500,21 +12720,21 @@ fn analyzeSwitchRuntimeBlock(
|
|||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
case_block.error_return_trace_index = child_block.error_return_trace_index;
|
||||
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
|
||||
if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
|
||||
emit_bb = true;
|
||||
|
||||
const prong_hint = try spa.analyzeProngRuntime(
|
||||
&case_block,
|
||||
.special,
|
||||
special.body,
|
||||
special.capture,
|
||||
else_prong.body,
|
||||
else_prong.capture,
|
||||
child_block.src(.{ .switch_capture = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
|
||||
.case_idx = .special_else,
|
||||
} }),
|
||||
&.{.bool_false},
|
||||
.bool_false,
|
||||
special.has_tag_capture,
|
||||
else_prong.has_tag_capture,
|
||||
);
|
||||
try branch_hints.append(gpa, prong_hint);
|
||||
|
||||
|
|
@ -12530,7 +12750,7 @@ fn analyzeSwitchRuntimeBlock(
|
|||
cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
|
||||
}
|
||||
},
|
||||
else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
|
||||
else => return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
|
||||
operand_ty.fmt(pt),
|
||||
}),
|
||||
};
|
||||
|
|
@ -12539,7 +12759,7 @@ fn analyzeSwitchRuntimeBlock(
|
|||
case_block.error_return_trace_index = child_block.error_return_trace_index;
|
||||
|
||||
if (zcu.backendSupportsFeature(.is_named_enum_value) and
|
||||
special.body.len != 0 and block.wantSafety() and
|
||||
else_prong.body.len != 0 and block.wantSafety() and
|
||||
operand_ty.zigTypeTag(zcu) == .@"enum" and
|
||||
(!operand_ty.isNonexhaustiveEnum(zcu) or union_originally))
|
||||
{
|
||||
|
|
@ -12548,7 +12768,12 @@ fn analyzeSwitchRuntimeBlock(
|
|||
try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
|
||||
}
|
||||
|
||||
const analyze_body = if (union_originally and !special.is_inline)
|
||||
const else_src_idx: LazySrcLoc.Offset.SwitchCaseIndex = if (else_prong_is_underscore)
|
||||
.special_under
|
||||
else
|
||||
.special_else;
|
||||
|
||||
const analyze_body = if (union_originally and !else_prong.is_inline)
|
||||
for (seen_enum_fields, 0..) |seen_field, index| {
|
||||
if (seen_field != null) continue;
|
||||
const union_obj = zcu.typeToUnion(maybe_union_ty).?;
|
||||
|
|
@ -12557,20 +12782,20 @@ fn analyzeSwitchRuntimeBlock(
|
|||
} else false
|
||||
else
|
||||
true;
|
||||
const else_hint: std.builtin.BranchHint = if (special.body.len != 0 and err_set and
|
||||
try sema.maybeErrorUnwrap(&case_block, special.body, operand, operand_src, allow_err_code_unwrap))
|
||||
const else_hint: std.builtin.BranchHint = if (else_prong.body.len != 0 and err_set and
|
||||
try sema.maybeErrorUnwrap(&case_block, else_prong.body, operand, operand_src, allow_err_code_unwrap))
|
||||
h: {
|
||||
// nothing to do here. weight against error branch
|
||||
break :h .unlikely;
|
||||
} else if (special.body.len != 0 and analyze_body and !special.is_inline) h: {
|
||||
} else if (else_prong.body.len != 0 and analyze_body and !else_prong.is_inline) h: {
|
||||
break :h try spa.analyzeProngRuntime(
|
||||
&case_block,
|
||||
.special,
|
||||
special.body,
|
||||
special.capture,
|
||||
else_prong.body,
|
||||
else_prong.capture,
|
||||
child_block.src(.{ .switch_capture = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
|
||||
.case_idx = else_src_idx,
|
||||
} }),
|
||||
undefined, // case_vals may be undefined for special prongs
|
||||
.none,
|
||||
|
|
@ -12644,7 +12869,9 @@ fn resolveSwitchComptimeLoop(
|
|||
cond_ty: Type,
|
||||
init_cond_val: Value,
|
||||
switch_node_offset: std.zig.Ast.Node.Offset,
|
||||
special: SpecialProng,
|
||||
special_members_only: ?SpecialProng,
|
||||
special_generic: SpecialProng,
|
||||
special_generic_is_under: bool,
|
||||
case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
|
||||
scalar_cases_len: u32,
|
||||
multi_cases_len: u32,
|
||||
|
|
@ -12664,7 +12891,9 @@ fn resolveSwitchComptimeLoop(
|
|||
cond_val,
|
||||
cond_ty,
|
||||
switch_node_offset,
|
||||
special,
|
||||
special_members_only,
|
||||
special_generic,
|
||||
special_generic_is_under,
|
||||
case_vals,
|
||||
scalar_cases_len,
|
||||
multi_cases_len,
|
||||
|
|
@ -12712,17 +12941,20 @@ fn resolveSwitchComptime(
|
|||
operand_val: Value,
|
||||
operand_ty: Type,
|
||||
switch_node_offset: std.zig.Ast.Node.Offset,
|
||||
special: SpecialProng,
|
||||
special_members_only: ?SpecialProng,
|
||||
special_generic: SpecialProng,
|
||||
special_generic_is_under: bool,
|
||||
case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
|
||||
scalar_cases_len: u32,
|
||||
multi_cases_len: u32,
|
||||
err_set: bool,
|
||||
empty_enum: bool,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const zcu = sema.pt.zcu;
|
||||
const merges = &child_block.label.?.merges;
|
||||
const resolved_operand_val = try sema.resolveLazyValue(operand_val);
|
||||
|
||||
var extra_index: usize = special.end;
|
||||
var extra_index: usize = special_generic.end;
|
||||
{
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
|
|
@ -12823,23 +13055,45 @@ fn resolveSwitchComptime(
|
|||
extra_index += info.body_len;
|
||||
}
|
||||
}
|
||||
if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special.body, cond_operand);
|
||||
if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special_generic.body, cond_operand);
|
||||
if (empty_enum) {
|
||||
return .void_value;
|
||||
}
|
||||
if (special_members_only) |special| {
|
||||
assert(operand_ty.isNonexhaustiveEnum(zcu));
|
||||
if (operand_ty.enumTagFieldIndex(operand_val, zcu)) |_| {
|
||||
return spa.resolveProngComptime(
|
||||
child_block,
|
||||
.special,
|
||||
special.body,
|
||||
special.capture,
|
||||
child_block.src(.{ .switch_capture = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = .special_else,
|
||||
} }),
|
||||
undefined, // case_vals may be undefined for special prongs
|
||||
if (special.is_inline) cond_operand else .none,
|
||||
special.has_tag_capture,
|
||||
merges,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return spa.resolveProngComptime(
|
||||
child_block,
|
||||
.special,
|
||||
special.body,
|
||||
special.capture,
|
||||
special_generic.body,
|
||||
special_generic.capture,
|
||||
child_block.src(.{ .switch_capture = .{
|
||||
.switch_node_offset = switch_node_offset,
|
||||
.case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
|
||||
.case_idx = if (special_generic_is_under)
|
||||
.special_under
|
||||
else
|
||||
.special_else,
|
||||
} }),
|
||||
undefined, // case_vals may be undefined for special prongs
|
||||
if (special.is_inline) cond_operand else .none,
|
||||
special.has_tag_capture,
|
||||
if (special_generic.is_inline) cond_operand else .none,
|
||||
special_generic.has_tag_capture,
|
||||
merges,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
103
src/Zcu.zig
103
src/Zcu.zig
|
|
@ -1679,20 +1679,37 @@ pub const SrcLoc = struct {
|
|||
return tree.nodeToSpan(condition);
|
||||
},
|
||||
|
||||
.node_offset_switch_special_prong => |node_off| {
|
||||
.node_offset_switch_else_prong => |node_off| {
|
||||
const tree = try src_loc.file_scope.getTree(zcu);
|
||||
const switch_node = node_off.toAbsolute(src_loc.base_node);
|
||||
_, const extra_index = tree.nodeData(switch_node).node_and_extra;
|
||||
const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
|
||||
for (case_nodes) |case_node| {
|
||||
const case = tree.fullSwitchCase(case_node).?;
|
||||
const is_special = (case.ast.values.len == 0) or
|
||||
(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])), "_"));
|
||||
if (!is_special) continue;
|
||||
if (case.ast.values.len == 0) {
|
||||
return tree.nodeToSpan(case_node);
|
||||
}
|
||||
} else unreachable;
|
||||
},
|
||||
|
||||
return tree.nodeToSpan(case_node);
|
||||
.node_offset_switch_under_prong => |node_off| {
|
||||
const tree = try src_loc.file_scope.getTree(zcu);
|
||||
const switch_node = node_off.toAbsolute(src_loc.base_node);
|
||||
_, const extra_index = tree.nodeData(switch_node).node_and_extra;
|
||||
const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
|
||||
for (case_nodes) |case_node| {
|
||||
const case = tree.fullSwitchCase(case_node).?;
|
||||
for (case.ast.values) |val| {
|
||||
if (tree.nodeTag(val) == .identifier and
|
||||
mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_"))
|
||||
{
|
||||
return tree.tokensToSpan(
|
||||
tree.firstToken(case_node),
|
||||
tree.lastToken(case_node),
|
||||
tree.nodeMainToken(val),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else unreachable;
|
||||
},
|
||||
|
||||
|
|
@ -1703,12 +1720,6 @@ pub const SrcLoc = struct {
|
|||
const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
|
||||
for (case_nodes) |case_node| {
|
||||
const case = tree.fullSwitchCase(case_node).?;
|
||||
const is_special = (case.ast.values.len == 0) or
|
||||
(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])), "_"));
|
||||
if (is_special) continue;
|
||||
|
||||
for (case.ast.values) |item_node| {
|
||||
if (tree.nodeTag(item_node) == .switch_range) {
|
||||
return tree.nodeToSpan(item_node);
|
||||
|
|
@ -2113,28 +2124,35 @@ pub const SrcLoc = struct {
|
|||
|
||||
var multi_i: u32 = 0;
|
||||
var scalar_i: u32 = 0;
|
||||
const case = for (case_nodes) |case_node| {
|
||||
var underscore_node: Ast.Node.OptionalIndex = .none;
|
||||
const case = case: for (case_nodes) |case_node| {
|
||||
const case = tree.fullSwitchCase(case_node).?;
|
||||
const is_special = special: {
|
||||
if (case.ast.values.len == 0) break :special true;
|
||||
if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) == .identifier) {
|
||||
break :special mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_");
|
||||
if (case.ast.values.len == 0) {
|
||||
if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special_else) {
|
||||
break :case case;
|
||||
}
|
||||
break :special false;
|
||||
};
|
||||
if (is_special) {
|
||||
if (want_case_idx.isSpecial()) {
|
||||
break case;
|
||||
}
|
||||
continue;
|
||||
continue :case;
|
||||
}
|
||||
if (underscore_node == .none) for (case.ast.values) |val_node| {
|
||||
if (tree.nodeTag(val_node) == .identifier and
|
||||
mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val_node)), "_"))
|
||||
{
|
||||
underscore_node = val_node.toOptional();
|
||||
if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special_under) {
|
||||
break :case case;
|
||||
}
|
||||
continue :case;
|
||||
}
|
||||
};
|
||||
|
||||
const is_multi = case.ast.values.len != 1 or
|
||||
tree.nodeTag(case.ast.values[0]) == .switch_range;
|
||||
|
||||
switch (want_case_idx.kind) {
|
||||
.scalar => if (!is_multi and want_case_idx.index == scalar_i) break case,
|
||||
.multi => if (is_multi and want_case_idx.index == multi_i) break case,
|
||||
.scalar => if (!is_multi and want_case_idx.index == scalar_i)
|
||||
break :case case,
|
||||
.multi => if (is_multi and want_case_idx.index == multi_i)
|
||||
break :case case,
|
||||
}
|
||||
|
||||
if (is_multi) {
|
||||
|
|
@ -2148,7 +2166,10 @@ pub const SrcLoc = struct {
|
|||
.switch_case_item,
|
||||
.switch_case_item_range_first,
|
||||
.switch_case_item_range_last,
|
||||
=> |x| x.item_idx,
|
||||
=> |x| item_idx: {
|
||||
assert(want_case_idx != LazySrcLoc.Offset.SwitchCaseIndex.special_else);
|
||||
break :item_idx x.item_idx;
|
||||
},
|
||||
.switch_capture, .switch_tag_capture => {
|
||||
const start = switch (src_loc.lazy) {
|
||||
.switch_capture => case.payload_token.?,
|
||||
|
|
@ -2173,7 +2194,11 @@ pub const SrcLoc = struct {
|
|||
.single => {
|
||||
var item_i: 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;
|
||||
}
|
||||
if (item_i != want_item.index) {
|
||||
item_i += 1;
|
||||
continue;
|
||||
|
|
@ -2184,7 +2209,9 @@ pub const SrcLoc = struct {
|
|||
.range => {
|
||||
var range_i: u32 = 0;
|
||||
for (case.ast.values) |item_node| {
|
||||
if (tree.nodeTag(item_node) != .switch_range) continue;
|
||||
if (tree.nodeTag(item_node) != .switch_range) {
|
||||
continue;
|
||||
}
|
||||
if (range_i != want_item.index) {
|
||||
range_i += 1;
|
||||
continue;
|
||||
|
|
@ -2363,10 +2390,14 @@ pub const LazySrcLoc = struct {
|
|||
/// by taking this AST node index offset from the containing base node,
|
||||
/// which points to a switch expression AST node. Next, navigate to the operand.
|
||||
node_offset_switch_operand: Ast.Node.Offset,
|
||||
/// The source location points to the else/`_` prong of a switch expression, found
|
||||
/// The source location points to the else prong of a switch expression, found
|
||||
/// by taking this AST node index offset from the containing base node,
|
||||
/// which points to a switch expression AST node. Next, navigate to the else/`_` prong.
|
||||
node_offset_switch_special_prong: Ast.Node.Offset,
|
||||
/// which points to a switch expression AST node. Next, navigate to the else prong.
|
||||
node_offset_switch_else_prong: Ast.Node.Offset,
|
||||
/// The source location points to the `_` prong of a switch expression, found
|
||||
/// by taking this AST node index offset from the containing base node,
|
||||
/// which points to a switch expression AST node. Next, navigate to the `_` prong.
|
||||
node_offset_switch_under_prong: Ast.Node.Offset,
|
||||
/// The source location points to all the ranges of a switch expression, found
|
||||
/// by taking this AST node index offset from the containing base node,
|
||||
/// which points to a switch expression AST node. Next, navigate to any of the
|
||||
|
|
@ -2562,10 +2593,8 @@ pub const LazySrcLoc = struct {
|
|||
kind: enum(u1) { scalar, multi },
|
||||
index: u31,
|
||||
|
||||
pub const special: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32)));
|
||||
pub fn isSpecial(idx: SwitchCaseIndex) bool {
|
||||
return @as(u32, @bitCast(idx)) == @as(u32, @bitCast(special));
|
||||
}
|
||||
pub const special_else: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32)));
|
||||
pub const special_under: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32) - 1));
|
||||
};
|
||||
|
||||
pub const SwitchItemIndex = packed struct(u32) {
|
||||
|
|
|
|||
|
|
@ -2087,15 +2087,10 @@ const Writer = struct {
|
|||
|
||||
self.indent += 2;
|
||||
|
||||
else_prong: {
|
||||
const special_prong = extra.data.bits.specialProng();
|
||||
const prong_name = switch (special_prong) {
|
||||
.@"else" => "else",
|
||||
.under => "_",
|
||||
else => break :else_prong,
|
||||
};
|
||||
const special_prongs = extra.data.bits.special_prongs;
|
||||
|
||||
const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
|
||||
if (special_prongs.hasElse()) {
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
|
||||
const capture_text = switch (info.capture) {
|
||||
.none => "",
|
||||
.by_val => "by_val ",
|
||||
|
|
@ -2108,7 +2103,63 @@ const Writer = struct {
|
|||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.splatByteAll(' ', self.indent);
|
||||
try stream.print("{s}{s}{s} => ", .{ capture_text, inline_text, prong_name });
|
||||
try stream.print("{s}{s}else => ", .{ capture_text, inline_text });
|
||||
try self.writeBracedBody(stream, body);
|
||||
}
|
||||
|
||||
if (special_prongs.hasUnder()) {
|
||||
var single_item_ref: Zir.Inst.Ref = .none;
|
||||
var items_len: u32 = 0;
|
||||
var ranges_len: u32 = 0;
|
||||
if (special_prongs.hasOneAdditionalItem()) {
|
||||
single_item_ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
} else if (special_prongs.hasManyAdditionalItems()) {
|
||||
items_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
ranges_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
}
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const items = self.code.refSlice(extra_index, items_len);
|
||||
extra_index += items_len;
|
||||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.splatByteAll(' ', self.indent);
|
||||
switch (info.capture) {
|
||||
.none => {},
|
||||
.by_val => try stream.writeAll("by_val "),
|
||||
.by_ref => try stream.writeAll("by_ref "),
|
||||
}
|
||||
if (info.is_inline) try stream.writeAll("inline ");
|
||||
|
||||
try stream.writeAll("_");
|
||||
if (single_item_ref != .none) {
|
||||
try stream.writeAll(", ");
|
||||
try self.writeInstRef(stream, single_item_ref);
|
||||
}
|
||||
for (items) |item_ref| {
|
||||
try stream.writeAll(", ");
|
||||
try self.writeInstRef(stream, item_ref);
|
||||
}
|
||||
|
||||
var range_i: usize = 0;
|
||||
while (range_i < ranges_len) : (range_i += 1) {
|
||||
const item_first: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const item_last: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
|
||||
try stream.writeAll(", ");
|
||||
try self.writeInstRef(stream, item_first);
|
||||
try stream.writeAll("...");
|
||||
try self.writeInstRef(stream, item_last);
|
||||
}
|
||||
|
||||
const body = self.code.bodySlice(extra_index, info.body_len);
|
||||
extra_index += info.body_len;
|
||||
try stream.writeAll(" => ");
|
||||
try self.writeBracedBody(stream, body);
|
||||
}
|
||||
|
||||
|
|
@ -2116,9 +2167,9 @@ const Writer = struct {
|
|||
const scalar_cases_len = extra.data.bits.scalar_cases_len;
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
const item_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
|
||||
const item_ref: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body = self.code.bodySlice(extra_index, info.body_len);
|
||||
extra_index += info.body_len;
|
||||
|
|
@ -2143,7 +2194,7 @@ const Writer = struct {
|
|||
extra_index += 1;
|
||||
const ranges_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
|
||||
const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const items = self.code.refSlice(extra_index, items_len);
|
||||
extra_index += items_len;
|
||||
|
|
@ -2164,9 +2215,9 @@ const Writer = struct {
|
|||
|
||||
var range_i: usize = 0;
|
||||
while (range_i < ranges_len) : (range_i += 1) {
|
||||
const item_first = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
|
||||
const item_first: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const item_last = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
|
||||
const item_last: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
|
||||
if (range_i != 0 or items.len != 0) {
|
||||
|
|
|
|||
|
|
@ -1073,3 +1073,50 @@ test "switch on 8-bit mod result" {
|
|||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
test "switch on non-exhaustive enum" {
|
||||
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
|
||||
|
||||
const E = enum(u4) {
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
_,
|
||||
|
||||
fn doTheTest(e: @This()) !void {
|
||||
switch (e) {
|
||||
.a, .b => {},
|
||||
else => return error.TestFailed,
|
||||
}
|
||||
switch (e) {
|
||||
.a, .b => {},
|
||||
.c => return error.TestFailed,
|
||||
_ => return error.TestFailed,
|
||||
}
|
||||
switch (e) {
|
||||
.a, .b => {},
|
||||
.c, _ => return error.TestFailed,
|
||||
}
|
||||
switch (e) {
|
||||
.a => {},
|
||||
.b, .c, _ => return error.TestFailed,
|
||||
}
|
||||
switch (e) {
|
||||
.b => return error.TestFailed,
|
||||
else => {},
|
||||
_ => return error.TestFailed,
|
||||
}
|
||||
switch (e) {
|
||||
else => {},
|
||||
_ => return error.TestFailed,
|
||||
}
|
||||
switch (e) {
|
||||
inline else => {},
|
||||
_ => return error.TestFailed,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try E.doTheTest(.a);
|
||||
try comptime E.doTheTest(.a);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,3 +249,27 @@ test "switch loop on larger than pointer integer" {
|
|||
}
|
||||
try expect(entry == 3);
|
||||
}
|
||||
|
||||
test "switch loop on non-exhaustive enum" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
const E = enum(u8) { a, b, c, _ };
|
||||
|
||||
fn doTheTest() !void {
|
||||
var start: E = undefined;
|
||||
start = .a;
|
||||
const result: u32 = s: switch (start) {
|
||||
.a => continue :s .c,
|
||||
else => continue :s @enumFromInt(123),
|
||||
.b, _ => |x| break :s @intFromEnum(x),
|
||||
};
|
||||
try expect(result == 123);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
try comptime S.doTheTest();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
const E = enum(u8) {
|
||||
a,
|
||||
b,
|
||||
_,
|
||||
};
|
||||
const U = union(E) {
|
||||
a: i32,
|
||||
b: u32,
|
||||
};
|
||||
pub export fn entry1() void {
|
||||
const e: E = .b;
|
||||
switch (e) { // error: switch not handling the tag `b`
|
||||
.a, _ => {},
|
||||
}
|
||||
}
|
||||
pub export fn entry2() void {
|
||||
const u = U{ .a = 2 };
|
||||
switch (u) { // error: `_` prong not allowed when switching on tagged union
|
||||
.a => {},
|
||||
.b, _ => {},
|
||||
}
|
||||
}
|
||||
|
||||
// error
|
||||
//
|
||||
// :12:5: error: switch must handle all possibilities
|
||||
// :3:5: note: unhandled enumeration value: 'b'
|
||||
// :1:11: note: enum 'tmp.E' declared here
|
||||
// :18:5: error: '_' prong only allowed when switching on non-exhaustive enums
|
||||
// :20:13: note: '_' prong here
|
||||
// :18:5: note: consider using 'else'
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
const E = enum(u8) {
|
||||
a,
|
||||
b,
|
||||
_,
|
||||
};
|
||||
|
||||
export fn f(e: E) void {
|
||||
switch (e) {
|
||||
.a => {},
|
||||
inline _ => {},
|
||||
}
|
||||
}
|
||||
|
||||
export fn g(e: E) void {
|
||||
switch (e) {
|
||||
.a => {},
|
||||
else => {},
|
||||
inline _ => {},
|
||||
}
|
||||
}
|
||||
|
||||
// error
|
||||
//
|
||||
// :10:16: error: cannot inline '_' prong
|
||||
// :18:16: error: cannot inline '_' prong
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
const E = enum(u8) {
|
||||
a,
|
||||
b,
|
||||
_,
|
||||
};
|
||||
|
||||
export fn f(e: E) void {
|
||||
switch (e) {
|
||||
.a, .b, _ => {},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
// error
|
||||
//
|
||||
// :10:14: error: unreachable else prong; all explicit cases already handled
|
||||
|
|
@ -16,5 +16,5 @@ pub export fn entry() void {
|
|||
// target=native
|
||||
//
|
||||
// :7:5: error: '_' prong only allowed when switching on non-exhaustive enums
|
||||
// :10:11: note: '_' prong here
|
||||
// :10:9: note: '_' prong here
|
||||
// :7:5: note: consider using 'else'
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ pub export fn entry3() void {
|
|||
// :12:5: error: switch must handle all possibilities
|
||||
// :3:5: note: unhandled enumeration value: 'b'
|
||||
// :1:11: note: enum 'tmp.E' declared here
|
||||
// :19:5: error: switch on non-exhaustive enum must include 'else' or '_' prong
|
||||
// :19:5: error: switch on non-exhaustive enum must include 'else' or '_' prong or both
|
||||
// :26:5: error: '_' prong only allowed when switching on non-exhaustive enums
|
||||
// :29:11: note: '_' prong here
|
||||
// :29:9: note: '_' prong here
|
||||
// :26:5: note: consider using 'else'
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue