authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-08-13 13:54:15+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-08-13 13:54:15+01:00
log6e90ce25364b02555a3ca46013f85b2e80e98705
treeaa3fe7f082466ba5fde47450d1c40c163e667d59
parentb8124d9c0b01e8ac7cd0daf93a0ed018da5f2352
parentaaee26bb1914a3d4e385bd120515813ece80311d
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #24381 from Justus2308/switch-better-underscore

Enhance switch on non-exhaustive enums

12 files changed, 925 insertions(+), 322 deletions(-)

lib/std/zig/AstGen.zig+122-85
...@@ -7662,10 +7662,12 @@ fn switchExpr(...@@ -7662,10 +7662,12 @@ fn switchExpr(
7662 var scalar_cases_len: u32 = 0;7662 var scalar_cases_len: u32 = 0;
7663 var multi_cases_len: u32 = 0;7663 var multi_cases_len: u32 = 0;
7664 var inline_cases_len: u32 = 0;7664 var inline_cases_len: u32 = 0;
7665 var special_prong: Zir.SpecialProng = .none;7665 var else_case_node: Ast.Node.OptionalIndex = .none;
7666 var special_node: Ast.Node.OptionalIndex = .none;
7667 var else_src: ?Ast.TokenIndex = null;7666 var else_src: ?Ast.TokenIndex = null;
7667 var underscore_case_node: Ast.Node.OptionalIndex = .none;
7668 var underscore_node: Ast.Node.OptionalIndex = .none;
7668 var underscore_src: ?Ast.TokenIndex = null;7669 var underscore_src: ?Ast.TokenIndex = null;
7670 var underscore_additional_items: Zir.SpecialProngs.AdditionalItems = .none;
7669 for (case_nodes) |case_node| {7671 for (case_nodes) |case_node| {
7670 const case = tree.fullSwitchCase(case_node).?;7672 const case = tree.fullSwitchCase(case_node).?;
7671 if (case.payload_token) |payload_token| {7673 if (case.payload_token) |payload_token| {
...@@ -7686,7 +7688,8 @@ fn switchExpr(...@@ -7686,7 +7688,8 @@ fn switchExpr(
7686 any_non_inline_capture = true;7688 any_non_inline_capture = true;
7687 }7689 }
7688 }7690 }
7689 // Check for else/`_` prong.7691
7692 // Check for else prong.
7690 if (case.ast.values.len == 0) {7693 if (case.ast.values.len == 0) {
7691 const case_src = case.ast.arrow_token - 1;7694 const case_src = case.ast.arrow_token - 1;
7692 if (else_src) |src| {7695 if (else_src) |src| {
...@@ -7702,79 +7705,51 @@ fn switchExpr(...@@ -7702,79 +7705,51 @@ fn switchExpr(
7702 ),7705 ),
7703 },7706 },
7704 );7707 );
7705 } else if (underscore_src) |some_underscore| {
7706 return astgen.failNodeNotes(
7707 node,
7708 "else and '_' prong in switch expression",
7709 .{},
7710 &[_]u32{
7711 try astgen.errNoteTok(
7712 case_src,
7713 "else prong here",
7714 .{},
7715 ),
7716 try astgen.errNoteTok(
7717 some_underscore,
7718 "'_' prong here",
7719 .{},
7720 ),
7721 },
7722 );
7723 }7708 }
7724 special_node = case_node.toOptional();7709 else_case_node = case_node.toOptional();
7725 special_prong = .@"else";
7726 else_src = case_src;7710 else_src = case_src;
7727 continue;7711 continue;
7728 } else if (case.ast.values.len == 1 and
7729 tree.nodeTag(case.ast.values[0]) == .identifier and
7730 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_"))
7731 {
7732 const case_src = case.ast.arrow_token - 1;
7733 if (underscore_src) |src| {
7734 return astgen.failTokNotes(
7735 case_src,
7736 "multiple '_' prongs in switch expression",
7737 .{},
7738 &[_]u32{
7739 try astgen.errNoteTok(
7740 src,
7741 "previous '_' prong here",
7742 .{},
7743 ),
7744 },
7745 );
7746 } else if (else_src) |some_else| {
7747 return astgen.failNodeNotes(
7748 node,
7749 "else and '_' prong in switch expression",
7750 .{},
7751 &[_]u32{
7752 try astgen.errNoteTok(
7753 some_else,
7754 "else prong here",
7755 .{},
7756 ),
7757 try astgen.errNoteTok(
7758 case_src,
7759 "'_' prong here",
7760 .{},
7761 ),
7762 },
7763 );
7764 }
7765 if (case.inline_token != null) {
7766 return astgen.failTok(case_src, "cannot inline '_' prong", .{});
7767 }
7768 special_node = case_node.toOptional();
7769 special_prong = .under;
7770 underscore_src = case_src;
7771 continue;
7772 }7712 }
77737713
7714 // Check for '_' prong.
7715 var case_has_underscore = false;
7774 for (case.ast.values) |val| {7716 for (case.ast.values) |val| {
7775 if (tree.nodeTag(val) == .string_literal)7717 switch (tree.nodeTag(val)) {
7776 return astgen.failNode(val, "cannot switch on strings", .{});7718 .identifier => if (mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_")) {
7719 const val_src = tree.nodeMainToken(val);
7720 if (underscore_src) |src| {
7721 return astgen.failTokNotes(
7722 val_src,
7723 "multiple '_' prongs in switch expression",
7724 .{},
7725 &[_]u32{
7726 try astgen.errNoteTok(
7727 src,
7728 "previous '_' prong here",
7729 .{},
7730 ),
7731 },
7732 );
7733 }
7734 if (case.inline_token != null) {
7735 return astgen.failTok(val_src, "cannot inline '_' prong", .{});
7736 }
7737 underscore_case_node = case_node.toOptional();
7738 underscore_src = val_src;
7739 underscore_node = val.toOptional();
7740 underscore_additional_items = switch (case.ast.values.len) {
7741 0 => unreachable,
7742 1 => .none,
7743 2 => .one,
7744 else => .many,
7745 };
7746 case_has_underscore = true;
7747 },
7748 .string_literal => return astgen.failNode(val, "cannot switch on strings", .{}),
7749 else => {},
7750 }
7777 }7751 }
7752 if (case_has_underscore) continue;
77787753
7779 if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) {7754 if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) {
7780 scalar_cases_len += 1;7755 scalar_cases_len += 1;
...@@ -7786,6 +7761,14 @@ fn switchExpr(...@@ -7786,6 +7761,14 @@ fn switchExpr(
7786 }7761 }
7787 }7762 }
77887763
7764 const special_prongs: Zir.SpecialProngs = .init(
7765 else_src != null,
7766 underscore_src != null,
7767 underscore_additional_items,
7768 );
7769 const has_else = special_prongs.hasElse();
7770 const has_under = special_prongs.hasUnder();
7771
7789 const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };7772 const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
77907773
7791 astgen.advanceSourceCursorToNode(operand_node);7774 astgen.advanceSourceCursorToNode(operand_node);
...@@ -7806,7 +7789,9 @@ fn switchExpr(...@@ -7806,7 +7789,9 @@ fn switchExpr(
7806 const payloads = &astgen.scratch;7789 const payloads = &astgen.scratch;
7807 const scratch_top = astgen.scratch.items.len;7790 const scratch_top = astgen.scratch.items.len;
7808 const case_table_start = scratch_top;7791 const case_table_start = scratch_top;
7809 const scalar_case_table = case_table_start + @intFromBool(special_prong != .none);7792 const else_case_index = if (has_else) case_table_start else undefined;
7793 const under_case_index = if (has_under) case_table_start + @intFromBool(has_else) else undefined;
7794 const scalar_case_table = case_table_start + @intFromBool(has_else) + @intFromBool(has_under);
7810 const multi_case_table = scalar_case_table + scalar_cases_len;7795 const multi_case_table = scalar_case_table + scalar_cases_len;
7811 const case_table_end = multi_case_table + multi_cases_len;7796 const case_table_end = multi_case_table + multi_cases_len;
7812 try astgen.scratch.resize(gpa, case_table_end);7797 try astgen.scratch.resize(gpa, case_table_end);
...@@ -7938,14 +7923,33 @@ fn switchExpr(...@@ -7938,14 +7923,33 @@ fn switchExpr(
79387923
7939 const header_index: u32 = @intCast(payloads.items.len);7924 const header_index: u32 = @intCast(payloads.items.len);
7940 const body_len_index = if (is_multi_case) blk: {7925 const body_len_index = if (is_multi_case) blk: {
7941 payloads.items[multi_case_table + multi_case_index] = header_index;7926 if (case_node.toOptional() == underscore_case_node) {
7942 multi_case_index += 1;7927 payloads.items[under_case_index] = header_index;
7928 if (special_prongs.hasOneAdditionalItem()) {
7929 try payloads.resize(gpa, header_index + 2); // item, body_len
7930 const maybe_item_node = case.ast.values[0];
7931 const item_node = if (maybe_item_node.toOptional() == underscore_node)
7932 case.ast.values[1]
7933 else
7934 maybe_item_node;
7935 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
7936 payloads.items[header_index] = @intFromEnum(item_inst);
7937 break :blk header_index + 1;
7938 }
7939 } else {
7940 payloads.items[multi_case_table + multi_case_index] = header_index;
7941 multi_case_index += 1;
7942 }
7943 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len7943 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
79447944
7945 // items7945 // items
7946 var items_len: u32 = 0;7946 var items_len: u32 = 0;
7947 for (case.ast.values) |item_node| {7947 for (case.ast.values) |item_node| {
7948 if (tree.nodeTag(item_node) == .switch_range) continue;7948 if (item_node.toOptional() == underscore_node or
7949 tree.nodeTag(item_node) == .switch_range)
7950 {
7951 continue;
7952 }
7949 items_len += 1;7953 items_len += 1;
79507954
7951 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);7955 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
...@@ -7955,7 +7959,9 @@ fn switchExpr(...@@ -7955,7 +7959,9 @@ fn switchExpr(
7955 // ranges7959 // ranges
7956 var ranges_len: u32 = 0;7960 var ranges_len: u32 = 0;
7957 for (case.ast.values) |range| {7961 for (case.ast.values) |range| {
7958 if (tree.nodeTag(range) != .switch_range) continue;7962 if (tree.nodeTag(range) != .switch_range) {
7963 continue;
7964 }
7959 ranges_len += 1;7965 ranges_len += 1;
79607966
7961 const first_node, const last_node = tree.nodeData(range).node_and_node;7967 const first_node, const last_node = tree.nodeData(range).node_and_node;
...@@ -7969,8 +7975,13 @@ fn switchExpr(...@@ -7969,8 +7975,13 @@ fn switchExpr(
7969 payloads.items[header_index] = items_len;7975 payloads.items[header_index] = items_len;
7970 payloads.items[header_index + 1] = ranges_len;7976 payloads.items[header_index + 1] = ranges_len;
7971 break :blk header_index + 2;7977 break :blk header_index + 2;
7972 } else if (case_node.toOptional() == special_node) blk: {7978 } else if (case_node.toOptional() == else_case_node) blk: {
7973 payloads.items[case_table_start] = header_index;7979 payloads.items[else_case_index] = header_index;
7980 try payloads.resize(gpa, header_index + 1); // body_len
7981 break :blk header_index;
7982 } else if (case_node.toOptional() == underscore_case_node) blk: {
7983 assert(!special_prongs.hasAdditionalItems());
7984 payloads.items[under_case_index] = header_index;
7974 try payloads.resize(gpa, header_index + 1); // body_len7985 try payloads.resize(gpa, header_index + 1); // body_len
7975 break :blk header_index;7986 break :blk header_index;
7976 } else blk: {7987 } else blk: {
...@@ -8025,15 +8036,13 @@ fn switchExpr(...@@ -8025,15 +8036,13 @@ fn switchExpr(
8025 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len +8036 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len +
8026 @intFromBool(multi_cases_len != 0) +8037 @intFromBool(multi_cases_len != 0) +
8027 @intFromBool(any_has_tag_capture) +8038 @intFromBool(any_has_tag_capture) +
8028 payloads.items.len - case_table_end +8039 payloads.items.len - scratch_top);
8029 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len);
80308040
8031 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{8041 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
8032 .operand = raw_operand,8042 .operand = raw_operand,
8033 .bits = Zir.Inst.SwitchBlock.Bits{8043 .bits = Zir.Inst.SwitchBlock.Bits{
8034 .has_multi_cases = multi_cases_len != 0,8044 .has_multi_cases = multi_cases_len != 0,
8035 .has_else = special_prong == .@"else",8045 .special_prongs = special_prongs,
8036 .has_under = special_prong == .under,
8037 .any_has_tag_capture = any_has_tag_capture,8046 .any_has_tag_capture = any_has_tag_capture,
8038 .any_non_inline_capture = any_non_inline_capture,8047 .any_non_inline_capture = any_non_inline_capture,
8039 .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue,8048 .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue,
...@@ -8052,13 +8061,41 @@ fn switchExpr(...@@ -8052,13 +8061,41 @@ fn switchExpr(
8052 const zir_datas = astgen.instructions.items(.data);8061 const zir_datas = astgen.instructions.items(.data);
8053 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;8062 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
80548063
8055 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {8064 if (has_else) {
8065 const start_index = payloads.items[else_case_index];
8066 var end_index = start_index + 1;
8067 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[start_index]);
8068 end_index += prong_info.body_len;
8069 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
8070 }
8071 if (has_under) {
8072 const start_index = payloads.items[under_case_index];
8056 var body_len_index = start_index;8073 var body_len_index = start_index;
8057 var end_index = start_index;8074 var end_index = start_index;
8058 const table_index = case_table_start + i;8075 switch (underscore_additional_items) {
8059 if (table_index < scalar_case_table) {8076 .none => {
8060 end_index += 1;8077 end_index += 1;
8061 } else if (table_index < multi_case_table) {8078 },
8079 .one => {
8080 body_len_index += 1;
8081 end_index += 2;
8082 },
8083 .many => {
8084 body_len_index += 2;
8085 const items_len = payloads.items[start_index];
8086 const ranges_len = payloads.items[start_index + 1];
8087 end_index += 3 + items_len + 2 * ranges_len;
8088 },
8089 }
8090 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
8091 end_index += prong_info.body_len;
8092 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
8093 }
8094 for (payloads.items[scalar_case_table..case_table_end], 0..) |start_index, i| {
8095 var body_len_index = start_index;
8096 var end_index = start_index;
8097 const table_index = scalar_case_table + i;
8098 if (table_index < multi_case_table) {
8062 body_len_index += 1;8099 body_len_index += 1;
8063 end_index += 2;8100 end_index += 2;
8064 } else {8101 } else {
lib/std/zig/Zir.zig+116-27
...@@ -3226,20 +3226,32 @@ pub const Inst = struct {...@@ -3226,20 +3226,32 @@ pub const Inst = struct {
32263226
3227 /// 0. multi_cases_len: u32 // If has_multi_cases is set.3227 /// 0. multi_cases_len: u32 // If has_multi_cases is set.
3228 /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture.3228 /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture.
3229 /// 2. else_body { // If has_else or has_under is set.3229 /// 2. else_body { // If special_prong.hasElse() is set.
3230 /// info: ProngInfo,3230 /// info: ProngInfo,
3231 /// body member Index for every info.body_len3231 /// body member Index for every info.body_len
3232 /// }3232 /// }
3233 /// 3. scalar_cases: { // for every scalar_cases_len3233 /// 3. under_body { // If special_prong.hasUnder() is set.
3234 /// item: Ref, // If special_prong.hasOneAdditionalItem() is set.
3235 /// items_len: u32, // If special_prong.hasManyAdditionalItems() is set.
3236 /// ranges_len: u32, // If special_prong.hasManyAdditionalItems() is set.
3237 /// info: ProngInfo,
3238 /// item: Ref, // for every items_len
3239 /// ranges: { // for every ranges_len
3240 /// item_first: Ref,
3241 /// item_last: Ref,
3242 /// }
3243 /// body member Index for every info.body_len
3244 /// }
3245 /// 4. scalar_cases: { // for every scalar_cases_len
3234 /// item: Ref,3246 /// item: Ref,
3235 /// info: ProngInfo,3247 /// info: ProngInfo,
3236 /// body member Index for every info.body_len3248 /// body member Index for every info.body_len
3237 /// }3249 /// }
3238 /// 4. multi_cases: { // for every multi_cases_len3250 /// 5. multi_cases: { // for every multi_cases_len
3239 /// items_len: u32,3251 /// items_len: u32,
3240 /// ranges_len: u32,3252 /// ranges_len: u32,
3241 /// info: ProngInfo,3253 /// info: ProngInfo,
3242 /// item: Ref // for every items_len3254 /// item: Ref, // for every items_len
3243 /// ranges: { // for every ranges_len3255 /// ranges: { // for every ranges_len
3244 /// item_first: Ref,3256 /// item_first: Ref,
3245 /// item_last: Ref,3257 /// item_last: Ref,
...@@ -3275,30 +3287,18 @@ pub const Inst = struct {...@@ -3275,30 +3287,18 @@ pub const Inst = struct {
3275 pub const Bits = packed struct(u32) {3287 pub const Bits = packed struct(u32) {
3276 /// If true, one or more prongs have multiple items.3288 /// If true, one or more prongs have multiple items.
3277 has_multi_cases: bool,3289 has_multi_cases: bool,
3278 /// If true, there is an else prong. This is mutually exclusive with `has_under`.3290 /// Information about the special prong.
3279 has_else: bool,3291 special_prongs: SpecialProngs,
3280 /// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
3281 has_under: bool,
3282 /// If true, at least one prong has an inline tag capture.3292 /// If true, at least one prong has an inline tag capture.
3283 any_has_tag_capture: bool,3293 any_has_tag_capture: bool,
3284 /// If true, at least one prong has a capture which may not3294 /// If true, at least one prong has a capture which may not
3285 /// be comptime-known via `inline`.3295 /// be comptime-known via `inline`.
3286 any_non_inline_capture: bool,3296 any_non_inline_capture: bool,
3297 /// If true, at least one prong contains a `continue`.
3287 has_continue: bool,3298 has_continue: bool,
3288 scalar_cases_len: ScalarCasesLen,3299 scalar_cases_len: ScalarCasesLen,
32893300
3290 pub const ScalarCasesLen = u26;3301 pub const ScalarCasesLen = u25;
3291
3292 pub fn specialProng(bits: Bits) SpecialProng {
3293 const has_else: u2 = @intFromBool(bits.has_else);
3294 const has_under: u2 = @intFromBool(bits.has_under);
3295 return switch ((has_else << 1) | has_under) {
3296 0b00 => .none,
3297 0b01 => .under,
3298 0b10 => .@"else",
3299 0b11 => unreachable,
3300 };
3301 }
3302 };3302 };
33033303
3304 pub const MultiProng = struct {3304 pub const MultiProng = struct {
...@@ -3874,7 +3874,68 @@ pub const Inst = struct {...@@ -3874,7 +3874,68 @@ pub const Inst = struct {
3874 };3874 };
3875};3875};
38763876
3877pub const SpecialProng = enum { none, @"else", under };3877pub const SpecialProngs = enum(u3) {
3878 none = 0b000,
3879 /// Simple `else` prong.
3880 /// `else => {},`
3881 @"else" = 0b001,
3882 /// Simple `_` prong.
3883 /// `_ => {},`
3884 under = 0b010,
3885 /// Both an `else` and a `_` prong.
3886 /// `else => {},`
3887 /// `_ => {},`
3888 under_and_else = 0b011,
3889 /// `_` prong with 1 additional item.
3890 /// `a, _ => {},`
3891 under_one_item = 0b100,
3892 /// Both an `else` and a `_` prong with 1 additional item.
3893 /// `else => {},`
3894 /// `a, _ => {},`
3895 under_one_item_and_else = 0b101,
3896 /// `_` prong with >1 additional items.
3897 /// `a, _, b => {},`
3898 under_many_items = 0b110,
3899 /// Both an `else` and a `_` prong with >1 additional items.
3900 /// `else => {},`
3901 /// `a, _, b => {},`
3902 under_many_items_and_else = 0b111,
3903
3904 pub const AdditionalItems = enum(u3) {
3905 none = @intFromEnum(SpecialProngs.under),
3906 one = @intFromEnum(SpecialProngs.under_one_item),
3907 many = @intFromEnum(SpecialProngs.under_many_items),
3908 };
3909
3910 pub fn init(has_else: bool, has_under: bool, additional_items: AdditionalItems) SpecialProngs {
3911 const else_bit: u3 = @intFromBool(has_else);
3912 const under_bits: u3 = if (has_under)
3913 @intFromEnum(additional_items)
3914 else
3915 @intFromEnum(SpecialProngs.none);
3916 return @enumFromInt(else_bit | under_bits);
3917 }
3918
3919 pub fn hasElse(special_prongs: SpecialProngs) bool {
3920 return (@intFromEnum(special_prongs) & 0b001) != 0;
3921 }
3922
3923 pub fn hasUnder(special_prongs: SpecialProngs) bool {
3924 return (@intFromEnum(special_prongs) & 0b110) != 0;
3925 }
3926
3927 pub fn hasAdditionalItems(special_prongs: SpecialProngs) bool {
3928 return (@intFromEnum(special_prongs) & 0b100) != 0;
3929 }
3930
3931 pub fn hasOneAdditionalItem(special_prongs: SpecialProngs) bool {
3932 return (@intFromEnum(special_prongs) & 0b110) == @intFromEnum(SpecialProngs.under_one_item);
3933 }
3934
3935 pub fn hasManyAdditionalItems(special_prongs: SpecialProngs) bool {
3936 return (@intFromEnum(special_prongs) & 0b110) == @intFromEnum(SpecialProngs.under_many_items);
3937 }
3938};
38783939
3879pub const DeclIterator = struct {3940pub const DeclIterator = struct {
3880 extra_index: u32,3941 extra_index: u32,
...@@ -4718,7 +4779,7 @@ fn findTrackableSwitch(...@@ -4718,7 +4779,7 @@ fn findTrackableSwitch(
4718 }4779 }
47194780
4720 const has_special = switch (kind) {4781 const has_special = switch (kind) {
4721 .normal => extra.data.bits.specialProng() != .none,4782 .normal => extra.data.bits.special_prongs != .none,
4722 .err_union => has_special: {4783 .err_union => has_special: {
4723 // Handle `non_err_body` first.4784 // Handle `non_err_body` first.
4724 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);4785 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
...@@ -4733,12 +4794,40 @@ fn findTrackableSwitch(...@@ -4733,12 +4794,40 @@ fn findTrackableSwitch(
4733 };4794 };
47344795
4735 if (has_special) {4796 if (has_special) {
4736 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);4797 const has_else = if (kind == .normal)
4737 extra_index += 1;4798 extra.data.bits.special_prongs.hasElse()
4738 const body = zir.bodySlice(extra_index, prong_info.body_len);4799 else
4739 extra_index += body.len;4800 true;
4801 if (has_else) {
4802 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
4803 extra_index += 1;
4804 const body = zir.bodySlice(extra_index, prong_info.body_len);
4805 extra_index += body.len;
47404806
4741 try zir.findTrackableBody(gpa, contents, defers, body);4807 try zir.findTrackableBody(gpa, contents, defers, body);
4808 }
4809 if (kind == .normal) {
4810 const special_prongs = extra.data.bits.special_prongs;
4811
4812 if (special_prongs.hasUnder()) {
4813 var trailing_items_len: u32 = 0;
4814 if (special_prongs.hasOneAdditionalItem()) {
4815 extra_index += 1;
4816 } else if (special_prongs.hasManyAdditionalItems()) {
4817 const items_len = zir.extra[extra_index];
4818 extra_index += 1;
4819 const ranges_len = zir.extra[extra_index];
4820 extra_index += 1;
4821 trailing_items_len = items_len + ranges_len * 2;
4822 }
4823 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
4824 extra_index += 1 + trailing_items_len;
4825 const body = zir.bodySlice(extra_index, prong_info.body_len);
4826 extra_index += body.len;
4827
4828 try zir.findTrackableBody(gpa, contents, defers, body);
4829 }
4830 }
4742 }4831 }
47434832
4744 {4833 {
src/Sema.zig+410-156
...@@ -10927,7 +10927,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -10927,7 +10927,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
10927 const switch_src = block.nodeOffset(inst_data.src_node);10927 const switch_src = block.nodeOffset(inst_data.src_node);
10928 const switch_src_node_offset = inst_data.src_node;10928 const switch_src_node_offset = inst_data.src_node;
10929 const switch_operand_src = block.src(.{ .node_offset_switch_operand = switch_src_node_offset });10929 const switch_operand_src = block.src(.{ .node_offset_switch_operand = switch_src_node_offset });
10930 const else_prong_src = block.src(.{ .node_offset_switch_special_prong = switch_src_node_offset });10930 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = switch_src_node_offset });
10931 const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);10931 const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);
10932 const main_operand_src = block.src(.{ .node_offset_if_cond = extra.data.main_src_node_offset });10932 const main_operand_src = block.src(.{ .node_offset_if_cond = extra.data.main_src_node_offset });
10933 const main_src = block.src(.{ .node_offset_main_token = extra.data.main_src_node_offset });10933 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...@@ -11121,6 +11121,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
11121 err_val,11121 err_val,
11122 operand_err_set_ty,11122 operand_err_set_ty,
11123 switch_src_node_offset,11123 switch_src_node_offset,
11124 null,
11124 .{11125 .{
11125 .body = else_case.body,11126 .body = else_case.body,
11126 .end = else_case.end,11127 .end = else_case.end,
...@@ -11128,6 +11129,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11128,6 +11129,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
11128 .is_inline = else_case.is_inline,11129 .is_inline = else_case.is_inline,
11129 .has_tag_capture = false,11130 .has_tag_capture = false,
11130 },11131 },
11132 false,
11131 case_vals,11133 case_vals,
11132 scalar_cases_len,11134 scalar_cases_len,
11133 multi_cases_len,11135 multi_cases_len,
...@@ -11199,6 +11201,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11199,6 +11201,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
11199 true,11201 true,
11200 switch_src_node_offset,11202 switch_src_node_offset,
11201 else_prong_src,11203 else_prong_src,
11204 false,
11202 undefined,11205 undefined,
11203 seen_errors,11206 seen_errors,
11204 undefined,11207 undefined,
...@@ -11206,6 +11209,10 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11206,6 +11209,10 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
11206 undefined,11209 undefined,
11207 cond_dbg_node_index,11210 cond_dbg_node_index,
11208 true,11211 true,
11212 null,
11213 undefined,
11214 &.{},
11215 &.{},
11209 );11216 );
1121011217
11211 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +11218 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...@@ -11242,12 +11249,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1124211249
11243 const pt = sema.pt;11250 const pt = sema.pt;
11244 const zcu = pt.zcu;11251 const zcu = pt.zcu;
11252 const ip = &zcu.intern_pool;
11245 const gpa = sema.gpa;11253 const gpa = sema.gpa;
11246 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;11254 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
11247 const src = block.nodeOffset(inst_data.src_node);11255 const src = block.nodeOffset(inst_data.src_node);
11248 const src_node_offset = inst_data.src_node;11256 const src_node_offset = inst_data.src_node;
11249 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });11257 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
11250 const special_prong_src = block.src(.{ .node_offset_switch_special_prong = src_node_offset });11258 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
11259 const under_prong_src = block.src(.{ .node_offset_switch_under_prong = src_node_offset });
11251 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);11260 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
1125211261
11253 const operand: SwitchProngAnalysis.Operand, const raw_operand_ty: Type = op: {11262 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...@@ -11334,27 +11343,63 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11334 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);11343 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
11335 defer case_vals.deinit(gpa);11344 defer case_vals.deinit(gpa);
1133611345
11337 const special_prong = extra.data.bits.specialProng();11346 var single_absorbed_item: Zir.Inst.Ref = .none;
11338 const special: SpecialProng = switch (special_prong) {11347 var absorbed_items: []const Zir.Inst.Ref = &.{};
11339 .none => .{11348 var absorbed_ranges: []const Zir.Inst.Ref = &.{};
11340 .body = &.{},11349
11341 .end = header_extra_index,11350 const special_prongs = extra.data.bits.special_prongs;
11342 .capture = .none,11351 const has_else = special_prongs.hasElse();
11343 .is_inline = false,11352 const has_under = special_prongs.hasUnder();
11344 .has_tag_capture = false,11353 const special_else: SpecialProng = if (has_else) blk: {
11345 },11354 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
11346 .under, .@"else" => blk: {11355 const extra_body_start = header_extra_index + 1;
11347 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);11356 break :blk .{
11348 const extra_body_start = header_extra_index + 1;11357 .body = sema.code.bodySlice(extra_body_start, info.body_len),
11349 break :blk .{11358 .end = extra_body_start + info.body_len,
11350 .body = sema.code.bodySlice(extra_body_start, info.body_len),11359 .capture = info.capture,
11351 .end = extra_body_start + info.body_len,11360 .is_inline = info.is_inline,
11352 .capture = info.capture,11361 .has_tag_capture = info.has_tag_capture,
11353 .is_inline = info.is_inline,11362 };
11354 .has_tag_capture = info.has_tag_capture,11363 } else .{
11355 };11364 .body = &.{},
11356 },11365 .end = header_extra_index,
11366 .capture = .none,
11367 .is_inline = false,
11368 .has_tag_capture = false,
11357 };11369 };
11370 const special_under: SpecialProng = if (has_under) blk: {
11371 var extra_index = special_else.end;
11372 var trailing_items_len: usize = 0;
11373 if (special_prongs.hasOneAdditionalItem()) {
11374 single_absorbed_item = @enumFromInt(sema.code.extra[extra_index]);
11375 extra_index += 1;
11376 absorbed_items = @ptrCast(&single_absorbed_item);
11377 } else if (special_prongs.hasManyAdditionalItems()) {
11378 const items_len = sema.code.extra[extra_index];
11379 extra_index += 1;
11380 const ranges_len = sema.code.extra[extra_index];
11381 extra_index += 1;
11382 absorbed_items = sema.code.refSlice(extra_index + 1, items_len);
11383 absorbed_ranges = sema.code.refSlice(extra_index + 1 + items_len, ranges_len * 2);
11384 trailing_items_len = items_len + ranges_len * 2;
11385 }
11386 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11387 extra_index += 1 + trailing_items_len;
11388 break :blk .{
11389 .body = sema.code.bodySlice(extra_index, info.body_len),
11390 .end = extra_index + info.body_len,
11391 .capture = info.capture,
11392 .is_inline = info.is_inline,
11393 .has_tag_capture = info.has_tag_capture,
11394 };
11395 } else .{
11396 .body = &.{},
11397 .end = special_else.end,
11398 .capture = .none,
11399 .is_inline = false,
11400 .has_tag_capture = false,
11401 };
11402 const special_end = special_under.end;
1135811403
11359 // Duplicate checking variables later also used for `inline else`.11404 // Duplicate checking variables later also used for `inline else`.
11360 var seen_enum_fields: []?LazySrcLoc = &.{};11405 var seen_enum_fields: []?LazySrcLoc = &.{};
...@@ -11374,7 +11419,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11374,7 +11419,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11374 var else_error_ty: ?Type = null;11419 var else_error_ty: ?Type = null;
1137511420
11376 // Validate usage of '_' prongs.11421 // Validate usage of '_' prongs.
11377 if (special_prong == .under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) {11422 if (has_under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) {
11378 const msg = msg: {11423 const msg = msg: {
11379 const msg = try sema.errMsg(11424 const msg = try sema.errMsg(
11380 src,11425 src,
...@@ -11383,7 +11428,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11383,7 +11428,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11383 );11428 );
11384 errdefer msg.destroy(gpa);11429 errdefer msg.destroy(gpa);
11385 try sema.errNote(11430 try sema.errNote(
11386 special_prong_src,11431 under_prong_src,
11387 msg,11432 msg,
11388 "'_' prong here",11433 "'_' prong here",
11389 .{},11434 .{},
...@@ -11408,7 +11453,23 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11408,7 +11453,23 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11408 @memset(seen_enum_fields, null);11453 @memset(seen_enum_fields, null);
11409 // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.11454 // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
1141011455
11411 var extra_index: usize = special.end;11456 for (absorbed_items, 0..) |item_ref, item_i| {
11457 _ = try sema.validateSwitchItemEnum(
11458 block,
11459 seen_enum_fields,
11460 &range_set,
11461 item_ref,
11462 cond_ty,
11463 block.src(.{ .switch_case_item = .{
11464 .switch_node_offset = src_node_offset,
11465 .case_idx = .special_under,
11466 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
11467 } }),
11468 );
11469 }
11470 try sema.validateSwitchNoRange(block, @intCast(absorbed_ranges.len), cond_ty, src_node_offset);
11471
11472 var extra_index: usize = special_end;
11412 {11473 {
11413 var scalar_i: u32 = 0;11474 var scalar_i: u32 = 0;
11414 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {11475 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...@@ -11466,13 +11527,22 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11466 if (seen_src == null) break false;11527 if (seen_src == null) break false;
11467 } else true;11528 } else true;
1146811529
11469 if (special_prong == .@"else") {11530 if (has_else) {
11470 if (all_tags_handled and !cond_ty.isNonexhaustiveEnum(zcu)) return sema.fail(11531 if (all_tags_handled) {
11471 block,11532 if (cond_ty.isNonexhaustiveEnum(zcu)) {
11472 special_prong_src,11533 if (has_under) return sema.fail(
11473 "unreachable else prong; all cases already handled",11534 block,
11474 .{},11535 else_prong_src,
11475 );11536 "unreachable else prong; all explicit cases already handled",
11537 .{},
11538 );
11539 } else return sema.fail(
11540 block,
11541 else_prong_src,
11542 "unreachable else prong; all cases already handled",
11543 .{},
11544 );
11545 }
11476 } else if (!all_tags_handled) {11546 } else if (!all_tags_handled) {
11477 const msg = msg: {11547 const msg = msg: {
11478 const msg = try sema.errMsg(11548 const msg = try sema.errMsg(
...@@ -11490,7 +11560,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11490,7 +11560,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11490 i,11560 i,
11491 msg,11561 msg,
11492 "unhandled enumeration value: '{f}'",11562 "unhandled enumeration value: '{f}'",
11493 .{field_name.fmt(&zcu.intern_pool)},11563 .{field_name.fmt(ip)},
11494 );11564 );
11495 }11565 }
11496 try sema.errNote(11566 try sema.errNote(
...@@ -11502,11 +11572,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11502,11 +11572,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11502 break :msg msg;11572 break :msg msg;
11503 };11573 };
11504 return sema.failWithOwnedErrorMsg(block, msg);11574 return sema.failWithOwnedErrorMsg(block, msg);
11505 } else if (special_prong == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) {11575 } else if (special_prongs == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
11506 return sema.fail(11576 return sema.fail(
11507 block,11577 block,
11508 src,11578 src,
11509 "switch on non-exhaustive enum must include 'else' or '_' prong",11579 "switch on non-exhaustive enum must include 'else' or '_' prong or both",
11510 .{},11580 .{},
11511 );11581 );
11512 }11582 }
...@@ -11520,11 +11590,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11520,11 +11590,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11520 inst_data,11590 inst_data,
11521 scalar_cases_len,11591 scalar_cases_len,
11522 multi_cases_len,11592 multi_cases_len,
11523 .{ .body = special.body, .end = special.end, .src = special_prong_src },11593 .{ .body = special_else.body, .end = special_else.end, .src = else_prong_src },
11524 special_prong == .@"else",11594 has_else,
11525 ),11595 ),
11526 .int, .comptime_int => {11596 .int, .comptime_int => {
11527 var extra_index: usize = special.end;11597 var extra_index: usize = special_end;
11528 {11598 {
11529 var scalar_i: u32 = 0;11599 var scalar_i: u32 = 0;
11530 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {11600 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...@@ -11606,10 +11676,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11606 const min_int = try cond_ty.minInt(pt, cond_ty);11676 const min_int = try cond_ty.minInt(pt, cond_ty);
11607 const max_int = try cond_ty.maxInt(pt, cond_ty);11677 const max_int = try cond_ty.maxInt(pt, cond_ty);
11608 if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {11678 if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {
11609 if (special_prong == .@"else") {11679 if (has_else) {
11610 return sema.fail(11680 return sema.fail(
11611 block,11681 block,
11612 special_prong_src,11682 else_prong_src,
11613 "unreachable else prong; all cases already handled",11683 "unreachable else prong; all cases already handled",
11614 .{},11684 .{},
11615 );11685 );
...@@ -11617,7 +11687,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11617,7 +11687,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11617 break :check_range;11687 break :check_range;
11618 }11688 }
11619 }11689 }
11620 if (special_prong != .@"else") {11690 if (special_prongs == .none) {
11621 return sema.fail(11691 return sema.fail(
11622 block,11692 block,
11623 src,11693 src,
...@@ -11628,7 +11698,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11628,7 +11698,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11628 }11698 }
11629 },11699 },
11630 .bool => {11700 .bool => {
11631 var extra_index: usize = special.end;11701 var extra_index: usize = special_end;
11632 {11702 {
11633 var scalar_i: u32 = 0;11703 var scalar_i: u32 = 0;
11634 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {11704 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...@@ -11680,31 +11750,28 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11680 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);11750 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
11681 }11751 }
11682 }11752 }
11683 switch (special_prong) {11753 if (has_else) {
11684 .@"else" => {11754 if (true_count + false_count == 2) {
11685 if (true_count + false_count == 2) {11755 return sema.fail(
11686 return sema.fail(11756 block,
11687 block,11757 else_prong_src,
11688 special_prong_src,11758 "unreachable else prong; all cases already handled",
11689 "unreachable else prong; all cases already handled",11759 .{},
11690 .{},11760 );
11691 );11761 }
11692 }11762 } else {
11693 },11763 if (true_count + false_count < 2) {
11694 .under, .none => {11764 return sema.fail(
11695 if (true_count + false_count < 2) {11765 block,
11696 return sema.fail(11766 src,
11697 block,11767 "switch must handle all possibilities",
11698 src,11768 .{},
11699 "switch must handle all possibilities",11769 );
11700 .{},11770 }
11701 );
11702 }
11703 },
11704 }11771 }
11705 },11772 },
11706 .enum_literal, .void, .@"fn", .pointer, .type => {11773 .enum_literal, .void, .@"fn", .pointer, .type => {
11707 if (special_prong != .@"else") {11774 if (!has_else) {
11708 return sema.fail(11775 return sema.fail(
11709 block,11776 block,
11710 src,11777 src,
...@@ -11716,7 +11783,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11716,7 +11783,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11716 var seen_values = ValueSrcMap{};11783 var seen_values = ValueSrcMap{};
11717 defer seen_values.deinit(gpa);11784 defer seen_values.deinit(gpa);
1171811785
11719 var extra_index: usize = special.end;11786 var extra_index: usize = special_end;
11720 {11787 {
11721 var scalar_i: u32 = 0;11788 var scalar_i: u32 = 0;
11722 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {11789 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...@@ -11789,6 +11856,16 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11789 }),11856 }),
11790 }11857 }
1179111858
11859 var special_members_only: ?SpecialProng = null;
11860 var special_members_only_src: LazySrcLoc = undefined;
11861 const special_generic, const special_generic_src = if (has_under) b: {
11862 if (has_else) {
11863 special_members_only = special_else;
11864 special_members_only_src = else_prong_src;
11865 }
11866 break :b .{ special_under, under_prong_src };
11867 } else .{ special_else, else_prong_src };
11868
11792 const spa: SwitchProngAnalysis = .{11869 const spa: SwitchProngAnalysis = .{
11793 .sema = sema,11870 .sema = sema,
11794 .parent_block = block,11871 .parent_block = block,
...@@ -11835,11 +11912,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11835,11 +11912,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11835 defer child_block.instructions.deinit(gpa);11912 defer child_block.instructions.deinit(gpa);
11836 defer merges.deinit(gpa);11913 defer merges.deinit(gpa);
1183711914
11838 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {11915 if (scalar_cases_len + multi_cases_len == 0 and
11916 special_members_only == null and
11917 !special_generic.is_inline)
11918 {
11839 if (empty_enum) {11919 if (empty_enum) {
11840 return .void_value;11920 return .void_value;
11841 }11921 }
11842 if (special_prong == .none) {11922 if (special_prongs == .none) {
11843 return sema.fail(block, src, "switch must handle all possibilities", .{});11923 return sema.fail(block, src, "switch must handle all possibilities", .{});
11844 }11924 }
11845 const init_cond = switch (operand) {11925 const init_cond = switch (operand) {
...@@ -11853,7 +11933,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11853,7 +11933,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11853 const ok = try block.addUnOp(.is_named_enum_value, init_cond);11933 const ok = try block.addUnOp(.is_named_enum_value, init_cond);
11854 try sema.addSafetyCheck(block, src, ok, .corrupt_switch);11934 try sema.addSafetyCheck(block, src, ok, .corrupt_switch);
11855 }11935 }
11856 if (err_set and try sema.maybeErrorUnwrap(block, special.body, init_cond, operand_src, false)) {11936 if (err_set and try sema.maybeErrorUnwrap(block, special_generic.body, init_cond, operand_src, false)) {
11857 return .unreachable_value;11937 return .unreachable_value;
11858 }11938 }
11859 }11939 }
...@@ -11873,7 +11953,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11873,7 +11953,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11873 cond_ty,11953 cond_ty,
11874 cond_val,11954 cond_val,
11875 src_node_offset,11955 src_node_offset,
11876 special,11956 special_members_only,
11957 special_generic,
11958 has_under,
11877 case_vals,11959 case_vals,
11878 scalar_cases_len,11960 scalar_cases_len,
11879 multi_cases_len,11961 multi_cases_len,
...@@ -11883,15 +11965,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11883,15 +11965,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11883 );11965 );
11884 }11966 }
1188511967
11886 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline and !extra.data.bits.has_continue) {11968 if (scalar_cases_len + multi_cases_len == 0 and
11969 special_members_only == null and
11970 !special_generic.is_inline and
11971 !extra.data.bits.has_continue)
11972 {
11887 return spa.resolveProngComptime(11973 return spa.resolveProngComptime(
11888 &child_block,11974 &child_block,
11889 .special,11975 .special,
11890 special.body,11976 special_generic.body,
11891 special.capture,11977 special_generic.capture,
11892 block.src(.{ .switch_capture = .{11978 block.src(.{ .switch_capture = .{
11893 .switch_node_offset = src_node_offset,11979 .switch_node_offset = src_node_offset,
11894 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,11980 .case_idx = if (has_under) .special_under else .special_else,
11895 } }),11981 } }),
11896 undefined, // case_vals may be undefined for special prongs11982 undefined, // case_vals may be undefined for special prongs
11897 .none,11983 .none,
...@@ -11907,6 +11993,87 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11907,6 +11993,87 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11907 unreachable;11993 unreachable;
11908 }11994 }
1190911995
11996 var extra_case_vals: struct {
11997 items: std.ArrayListUnmanaged(Air.Inst.Ref),
11998 ranges: std.ArrayListUnmanaged([2]Air.Inst.Ref),
11999 } = .{ .items = .empty, .ranges = .empty };
12000 defer {
12001 extra_case_vals.items.deinit(gpa);
12002 extra_case_vals.ranges.deinit(gpa);
12003 }
12004
12005 // Runtime switch, if we have a special_members_only prong we need to unroll
12006 // it to a prong with explicit items.
12007 // Although this is potentially the same as `inline else` it does not count
12008 // towards the backward branch quota because it's an implementation detail.
12009 if (special_members_only != null) gen: {
12010 assert(cond_ty.isNonexhaustiveEnum(zcu));
12011
12012 var min_i: usize = math.maxInt(usize);
12013 var max_i: usize = 0;
12014 var seen_field_count: usize = 0;
12015 for (seen_enum_fields, 0..) |seen, enum_i| {
12016 if (seen != null) {
12017 seen_field_count += 1;
12018 } else {
12019 min_i = @min(min_i, enum_i);
12020 max_i = @max(max_i, enum_i);
12021 }
12022 }
12023 if (min_i == max_i) {
12024 seen_enum_fields[min_i] = special_members_only_src;
12025 const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
12026 const item_ref = Air.internedToRef(item_val.toIntern());
12027 try extra_case_vals.items.append(gpa, item_ref);
12028 break :gen;
12029 }
12030 const missing_field_count = seen_enum_fields.len - seen_field_count;
12031
12032 extra_case_vals.items = try .initCapacity(gpa, missing_field_count / 2);
12033 extra_case_vals.ranges = try .initCapacity(gpa, missing_field_count / 4);
12034 const int_ty = cond_ty.intTagType(zcu);
12035
12036 var last_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
12037 var first_ref = Air.internedToRef(last_val.toIntern());
12038 seen_enum_fields[min_i] = special_members_only_src;
12039 for (seen_enum_fields[(min_i + 1)..(max_i + 1)], (min_i + 1)..) |seen, enum_i| {
12040 if (seen != null) continue;
12041 seen_enum_fields[enum_i] = special_members_only_src;
12042
12043 const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(enum_i));
12044 const item_ref = Air.internedToRef(item_val.toIntern());
12045
12046 const is_next = is_next: {
12047 const prev_int = ip.indexToKey(last_val.toIntern()).enum_tag.int;
12048
12049 const result = try arith.incrementDefinedInt(sema, int_ty, .fromInterned(prev_int));
12050 if (result.overflow) break :is_next false;
12051
12052 const item_int = ip.indexToKey(item_val.toIntern()).enum_tag.int;
12053 break :is_next try sema.valuesEqual(.fromInterned(item_int), result.val, int_ty);
12054 };
12055
12056 if (is_next) {
12057 last_val = item_val;
12058 } else {
12059 const last_ref = Air.internedToRef(last_val.toIntern());
12060 if (first_ref == last_ref) {
12061 try extra_case_vals.items.append(gpa, first_ref);
12062 } else {
12063 try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
12064 }
12065 first_ref = item_ref;
12066 last_val = item_val;
12067 }
12068 }
12069 const last_ref = Air.internedToRef(last_val.toIntern());
12070 if (first_ref == last_ref) {
12071 try extra_case_vals.items.append(gpa, first_ref);
12072 } else {
12073 try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
12074 }
12075 }
12076
11910 const air_switch_ref = try sema.analyzeSwitchRuntimeBlock(12077 const air_switch_ref = try sema.analyzeSwitchRuntimeBlock(
11911 spa,12078 spa,
11912 &child_block,12079 &child_block,
...@@ -11918,14 +12085,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11918,14 +12085,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11918 cond_ty,12085 cond_ty,
11919 operand_src,12086 operand_src,
11920 case_vals,12087 case_vals,
11921 special,12088 special_generic,
11922 scalar_cases_len,12089 scalar_cases_len,
11923 multi_cases_len,12090 multi_cases_len,
11924 union_originally,12091 union_originally,
11925 raw_operand_ty,12092 raw_operand_ty,
11926 err_set,12093 err_set,
11927 src_node_offset,12094 src_node_offset,
11928 special_prong_src,12095 special_generic_src,
12096 has_under,
11929 seen_enum_fields,12097 seen_enum_fields,
11930 seen_errors,12098 seen_errors,
11931 range_set,12099 range_set,
...@@ -11933,6 +12101,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11933,6 +12101,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11933 false_count,12101 false_count,
11934 cond_dbg_node_index,12102 cond_dbg_node_index,
11935 false,12103 false,
12104 special_members_only,
12105 special_members_only_src,
12106 extra_case_vals.items.items,
12107 extra_case_vals.ranges.items,
11936 );12108 );
1193712109
11938 for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {12110 for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {
...@@ -12016,14 +12188,15 @@ fn analyzeSwitchRuntimeBlock(...@@ -12016,14 +12188,15 @@ fn analyzeSwitchRuntimeBlock(
12016 operand_ty: Type,12188 operand_ty: Type,
12017 operand_src: LazySrcLoc,12189 operand_src: LazySrcLoc,
12018 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),12190 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
12019 special: SpecialProng,12191 else_prong: SpecialProng,
12020 scalar_cases_len: usize,12192 scalar_cases_len: usize,
12021 multi_cases_len: usize,12193 multi_cases_len: usize,
12022 union_originally: bool,12194 union_originally: bool,
12023 maybe_union_ty: Type,12195 maybe_union_ty: Type,
12024 err_set: bool,12196 err_set: bool,
12025 switch_node_offset: std.zig.Ast.Node.Offset,12197 switch_node_offset: std.zig.Ast.Node.Offset,
12026 special_prong_src: LazySrcLoc,12198 else_prong_src: LazySrcLoc,
12199 else_prong_is_underscore: bool,
12027 seen_enum_fields: []?LazySrcLoc,12200 seen_enum_fields: []?LazySrcLoc,
12028 seen_errors: SwitchErrorSet,12201 seen_errors: SwitchErrorSet,
12029 range_set: RangeSet,12202 range_set: RangeSet,
...@@ -12031,6 +12204,11 @@ fn analyzeSwitchRuntimeBlock(...@@ -12031,6 +12204,11 @@ fn analyzeSwitchRuntimeBlock(
12031 false_count: u8,12204 false_count: u8,
12032 cond_dbg_node_index: Zir.Inst.Index,12205 cond_dbg_node_index: Zir.Inst.Index,
12033 allow_err_code_unwrap: bool,12206 allow_err_code_unwrap: bool,
12207 extra_prong: ?SpecialProng,
12208 /// May be `undefined` if `extra_prong` is `null`
12209 extra_prong_src: LazySrcLoc,
12210 extra_prong_items: []const Air.Inst.Ref,
12211 extra_prong_ranges: []const [2]Air.Inst.Ref,
12034) CompileError!Air.Inst.Ref {12212) CompileError!Air.Inst.Ref {
12035 const pt = sema.pt;12213 const pt = sema.pt;
12036 const zcu = pt.zcu;12214 const zcu = pt.zcu;
...@@ -12054,7 +12232,7 @@ fn analyzeSwitchRuntimeBlock(...@@ -12054,7 +12232,7 @@ fn analyzeSwitchRuntimeBlock(
12054 case_block.need_debug_scope = null; // this body is emitted regardless12232 case_block.need_debug_scope = null; // this body is emitted regardless
12055 defer case_block.instructions.deinit(gpa);12233 defer case_block.instructions.deinit(gpa);
1205612234
12057 var extra_index: usize = special.end;12235 var extra_index: usize = else_prong.end;
1205812236
12059 var scalar_i: usize = 0;12237 var scalar_i: usize = 0;
12060 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {12238 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
...@@ -12116,23 +12294,42 @@ fn analyzeSwitchRuntimeBlock(...@@ -12116,23 +12294,42 @@ fn analyzeSwitchRuntimeBlock(
1211612294
12117 var cases_len = scalar_cases_len;12295 var cases_len = scalar_cases_len;
12118 var case_val_idx: usize = scalar_cases_len;12296 var case_val_idx: usize = scalar_cases_len;
12297 const multi_cases_len_with_extra_prong = multi_cases_len + @intFromBool(extra_prong != null);
12119 var multi_i: u32 = 0;12298 var multi_i: u32 = 0;
12120 while (multi_i < multi_cases_len) : (multi_i += 1) {12299 while (multi_i < multi_cases_len_with_extra_prong) : (multi_i += 1) {
12121 const items_len = sema.code.extra[extra_index];12300 const is_extra_prong = multi_i == multi_cases_len;
12122 extra_index += 1;12301 var items: []const Air.Inst.Ref = undefined;
12123 const ranges_len = sema.code.extra[extra_index];12302 var info: Zir.Inst.SwitchBlock.ProngInfo = undefined;
12124 extra_index += 1;12303 var ranges: []const [2]Air.Inst.Ref = undefined;
12125 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);12304 var body: []const Zir.Inst.Index = undefined;
12126 extra_index += 1 + items_len + 2 * ranges_len;12305 if (is_extra_prong) {
12306 const prong = extra_prong.?;
12307 items = extra_prong_items;
12308 ranges = extra_prong_ranges;
12309 body = prong.body;
12310 info = .{
12311 .body_len = undefined,
12312 .capture = prong.capture,
12313 .is_inline = prong.is_inline,
12314 .has_tag_capture = prong.has_tag_capture,
12315 };
12316 } else {
12317 @branchHint(.likely);
12318 const items_len = sema.code.extra[extra_index];
12319 extra_index += 1;
12320 const ranges_len = sema.code.extra[extra_index];
12321 extra_index += 1;
12322 info = @bitCast(sema.code.extra[extra_index]);
12323 extra_index += 1 + items_len + ranges_len * 2;
1212712324
12128 const items = case_vals.items[case_val_idx..][0..items_len];12325 items = case_vals.items[case_val_idx..][0..items_len];
12129 case_val_idx += items_len;12326 case_val_idx += items_len;
12130 // TODO: @ptrCast slice once Sema supports it12327 ranges = @ptrCast(case_vals.items[case_val_idx..][0 .. ranges_len * 2]);
12131 const ranges: []const [2]Air.Inst.Ref = @as([*]const [2]Air.Inst.Ref, @ptrCast(case_vals.items[case_val_idx..]))[0..ranges_len];12328 case_val_idx += ranges_len * 2;
12132 case_val_idx += ranges_len * 2;
1213312329
12134 const body = sema.code.bodySlice(extra_index, info.body_len);12330 body = sema.code.bodySlice(extra_index, info.body_len);
12135 extra_index += info.body_len;12331 extra_index += info.body_len;
12332 }
1213612333
12137 case_block.instructions.shrinkRetainingCapacity(0);12334 case_block.instructions.shrinkRetainingCapacity(0);
12138 case_block.error_return_trace_index = child_block.error_return_trace_index;12335 case_block.error_return_trace_index = child_block.error_return_trace_index;
...@@ -12142,14 +12339,29 @@ fn analyzeSwitchRuntimeBlock(...@@ -12142,14 +12339,29 @@ fn analyzeSwitchRuntimeBlock(
12142 var emit_bb = false;12339 var emit_bb = false;
1214312340
12144 for (ranges, 0..) |range_items, range_i| {12341 for (ranges, 0..) |range_items, range_i| {
12145 var item = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable;12342 var item = sema.resolveConstDefinedValue(block, .unneeded, range_items[0], undefined) catch unreachable;
12146 const item_last = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable;12343 const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_items[1], undefined) catch unreachable;
1214712344
12148 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({12345 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
12149 // Previous validation has resolved any possible lazy values.12346 // Previous validation has resolved any possible lazy values.
12150 const result = try arith.incrementDefinedInt(sema, operand_ty, item);12347 const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
12348 .int => .{ item, operand_ty },
12349 .@"enum" => b: {
12350 const int_val = Value.fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int);
12351 break :b .{ int_val, int_val.typeOf(zcu) };
12352 },
12353 else => unreachable,
12354 };
12355 const result = try arith.incrementDefinedInt(sema, int_ty, int_val);
12151 assert(!result.overflow);12356 assert(!result.overflow);
12152 item = result.val;12357 item = switch (operand_ty.zigTypeTag(zcu)) {
12358 .int => result.val,
12359 .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{
12360 .ty = operand_ty.toIntern(),
12361 .int = result.val.toIntern(),
12362 } })),
12363 else => unreachable,
12364 };
12153 }) {12365 }) {
12154 cases_len += 1;12366 cases_len += 1;
1215512367
...@@ -12158,11 +12370,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -12158,11 +12370,14 @@ fn analyzeSwitchRuntimeBlock(
12158 case_block.instructions.shrinkRetainingCapacity(0);12370 case_block.instructions.shrinkRetainingCapacity(0);
12159 case_block.error_return_trace_index = child_block.error_return_trace_index;12371 case_block.error_return_trace_index = child_block.error_return_trace_index;
1216012372
12161 if (emit_bb) try sema.emitBackwardBranch(block, block.src(.{ .switch_case_item = .{12373 if (emit_bb) {
12162 .switch_node_offset = switch_node_offset,12374 const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
12163 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },12375 .switch_node_offset = switch_node_offset,
12164 .item_idx = .{ .kind = .range, .index = @intCast(range_i) },12376 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12165 } }));12377 .item_idx = .{ .kind = .range, .index = @intCast(range_i) },
12378 } });
12379 try sema.emitBackwardBranch(block, bb_src);
12380 }
12166 emit_bb = true;12381 emit_bb = true;
1216712382
12168 const prong_hint = try spa.analyzeProngRuntime(12383 const prong_hint = try spa.analyzeProngRuntime(
...@@ -12207,11 +12422,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -12207,11 +12422,14 @@ fn analyzeSwitchRuntimeBlock(
12207 break :blk field_ty.zigTypeTag(zcu) != .noreturn;12422 break :blk field_ty.zigTypeTag(zcu) != .noreturn;
12208 } else true;12423 } else true;
1220912424
12210 if (emit_bb) try sema.emitBackwardBranch(block, block.src(.{ .switch_case_item = .{12425 if (emit_bb) {
12211 .switch_node_offset = switch_node_offset,12426 const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
12212 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },12427 .switch_node_offset = switch_node_offset,
12213 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },12428 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12214 } }));12429 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
12430 } });
12431 try sema.emitBackwardBranch(block, bb_src);
12432 }
12215 emit_bb = true;12433 emit_bb = true;
1221612434
12217 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {12435 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
...@@ -12287,11 +12505,11 @@ fn analyzeSwitchRuntimeBlock(...@@ -12287,11 +12505,11 @@ fn analyzeSwitchRuntimeBlock(
12287 try branch_hints.append(gpa, prong_hint);12505 try branch_hints.append(gpa, prong_hint);
1228812506
12289 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +12507 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12290 items.len + 2 * ranges_len +12508 items.len + ranges.len * 2 +
12291 case_block.instructions.items.len);12509 case_block.instructions.items.len);
12292 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{12510 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12293 .items_len = @intCast(items.len),12511 .items_len = @intCast(items.len),
12294 .ranges_len = @intCast(ranges_len),12512 .ranges_len = @intCast(ranges.len),
12295 .body_len = @intCast(case_block.instructions.items.len),12513 .body_len = @intCast(case_block.instructions.items.len),
12296 }));12514 }));
1229712515
...@@ -12308,12 +12526,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -12308,12 +12526,14 @@ fn analyzeSwitchRuntimeBlock(
12308 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));12526 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12309 }12527 }
1231012528
12311 const else_body: []const Air.Inst.Index = if (special.body.len != 0 or case_block.wantSafety()) else_body: {12529 const else_body: []const Air.Inst.Index = if (else_prong.body.len != 0 or case_block.wantSafety()) else_body: {
12312 var emit_bb = false;12530 var emit_bb = false;
12313 if (special.is_inline) switch (operand_ty.zigTypeTag(zcu)) {12531 // If this is true we must have a 'true' else prong and not an underscore because
12532 // underscore prongs can never be inlined. We've already checked for this.
12533 if (else_prong.is_inline) switch (operand_ty.zigTypeTag(zcu)) {
12314 .@"enum" => {12534 .@"enum" => {
12315 if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) {12535 if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
12316 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{12536 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
12317 operand_ty.fmt(pt),12537 operand_ty.fmt(pt),
12318 });12538 });
12319 }12539 }
...@@ -12332,22 +12552,22 @@ fn analyzeSwitchRuntimeBlock(...@@ -12332,22 +12552,22 @@ fn analyzeSwitchRuntimeBlock(
12332 break :blk field_ty.zigTypeTag(zcu) != .noreturn;12552 break :blk field_ty.zigTypeTag(zcu) != .noreturn;
12333 } else true;12553 } else true;
1233412554
12335 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);12555 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12336 emit_bb = true;12556 emit_bb = true;
1233712557
12338 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {12558 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
12339 break :h try spa.analyzeProngRuntime(12559 break :h try spa.analyzeProngRuntime(
12340 &case_block,12560 &case_block,
12341 .special,12561 .special,
12342 special.body,12562 else_prong.body,
12343 special.capture,12563 else_prong.capture,
12344 child_block.src(.{ .switch_capture = .{12564 child_block.src(.{ .switch_capture = .{
12345 .switch_node_offset = switch_node_offset,12565 .switch_node_offset = switch_node_offset,
12346 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,12566 .case_idx = .special_else,
12347 } }),12567 } }),
12348 &.{item_ref},12568 &.{item_ref},
12349 item_ref,12569 item_ref,
12350 special.has_tag_capture,12570 else_prong.has_tag_capture,
12351 );12571 );
12352 } else h: {12572 } else h: {
12353 _ = try case_block.addNoOp(.unreach);12573 _ = try case_block.addNoOp(.unreach);
...@@ -12369,7 +12589,7 @@ fn analyzeSwitchRuntimeBlock(...@@ -12369,7 +12589,7 @@ fn analyzeSwitchRuntimeBlock(
12369 },12589 },
12370 .error_set => {12590 .error_set => {
12371 if (operand_ty.isAnyError(zcu)) {12591 if (operand_ty.isAnyError(zcu)) {
12372 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{12592 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
12373 operand_ty.fmt(pt),12593 operand_ty.fmt(pt),
12374 });12594 });
12375 }12595 }
...@@ -12388,21 +12608,21 @@ fn analyzeSwitchRuntimeBlock(...@@ -12388,21 +12608,21 @@ fn analyzeSwitchRuntimeBlock(
12388 case_block.instructions.shrinkRetainingCapacity(0);12608 case_block.instructions.shrinkRetainingCapacity(0);
12389 case_block.error_return_trace_index = child_block.error_return_trace_index;12609 case_block.error_return_trace_index = child_block.error_return_trace_index;
1239012610
12391 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);12611 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12392 emit_bb = true;12612 emit_bb = true;
1239312613
12394 const prong_hint = try spa.analyzeProngRuntime(12614 const prong_hint = try spa.analyzeProngRuntime(
12395 &case_block,12615 &case_block,
12396 .special,12616 .special,
12397 special.body,12617 else_prong.body,
12398 special.capture,12618 else_prong.capture,
12399 child_block.src(.{ .switch_capture = .{12619 child_block.src(.{ .switch_capture = .{
12400 .switch_node_offset = switch_node_offset,12620 .switch_node_offset = switch_node_offset,
12401 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,12621 .case_idx = .special_else,
12402 } }),12622 } }),
12403 &.{item_ref},12623 &.{item_ref},
12404 item_ref,12624 item_ref,
12405 special.has_tag_capture,12625 else_prong.has_tag_capture,
12406 );12626 );
12407 try branch_hints.append(gpa, prong_hint);12627 try branch_hints.append(gpa, prong_hint);
1240812628
...@@ -12428,21 +12648,21 @@ fn analyzeSwitchRuntimeBlock(...@@ -12428,21 +12648,21 @@ fn analyzeSwitchRuntimeBlock(
12428 case_block.instructions.shrinkRetainingCapacity(0);12648 case_block.instructions.shrinkRetainingCapacity(0);
12429 case_block.error_return_trace_index = child_block.error_return_trace_index;12649 case_block.error_return_trace_index = child_block.error_return_trace_index;
1243012650
12431 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);12651 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12432 emit_bb = true;12652 emit_bb = true;
1243312653
12434 const prong_hint = try spa.analyzeProngRuntime(12654 const prong_hint = try spa.analyzeProngRuntime(
12435 &case_block,12655 &case_block,
12436 .special,12656 .special,
12437 special.body,12657 else_prong.body,
12438 special.capture,12658 else_prong.capture,
12439 child_block.src(.{ .switch_capture = .{12659 child_block.src(.{ .switch_capture = .{
12440 .switch_node_offset = switch_node_offset,12660 .switch_node_offset = switch_node_offset,
12441 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,12661 .case_idx = .special_else,
12442 } }),12662 } }),
12443 &.{item_ref},12663 &.{item_ref},
12444 item_ref,12664 item_ref,
12445 special.has_tag_capture,12665 else_prong.has_tag_capture,
12446 );12666 );
12447 try branch_hints.append(gpa, prong_hint);12667 try branch_hints.append(gpa, prong_hint);
1244812668
...@@ -12465,21 +12685,21 @@ fn analyzeSwitchRuntimeBlock(...@@ -12465,21 +12685,21 @@ fn analyzeSwitchRuntimeBlock(
12465 case_block.instructions.shrinkRetainingCapacity(0);12685 case_block.instructions.shrinkRetainingCapacity(0);
12466 case_block.error_return_trace_index = child_block.error_return_trace_index;12686 case_block.error_return_trace_index = child_block.error_return_trace_index;
1246712687
12468 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);12688 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12469 emit_bb = true;12689 emit_bb = true;
1247012690
12471 const prong_hint = try spa.analyzeProngRuntime(12691 const prong_hint = try spa.analyzeProngRuntime(
12472 &case_block,12692 &case_block,
12473 .special,12693 .special,
12474 special.body,12694 else_prong.body,
12475 special.capture,12695 else_prong.capture,
12476 child_block.src(.{ .switch_capture = .{12696 child_block.src(.{ .switch_capture = .{
12477 .switch_node_offset = switch_node_offset,12697 .switch_node_offset = switch_node_offset,
12478 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,12698 .case_idx = .special_else,
12479 } }),12699 } }),
12480 &.{.bool_true},12700 &.{.bool_true},
12481 .bool_true,12701 .bool_true,
12482 special.has_tag_capture,12702 else_prong.has_tag_capture,
12483 );12703 );
12484 try branch_hints.append(gpa, prong_hint);12704 try branch_hints.append(gpa, prong_hint);
1248512705
...@@ -12500,21 +12720,21 @@ fn analyzeSwitchRuntimeBlock(...@@ -12500,21 +12720,21 @@ fn analyzeSwitchRuntimeBlock(
12500 case_block.instructions.shrinkRetainingCapacity(0);12720 case_block.instructions.shrinkRetainingCapacity(0);
12501 case_block.error_return_trace_index = child_block.error_return_trace_index;12721 case_block.error_return_trace_index = child_block.error_return_trace_index;
1250212722
12503 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);12723 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12504 emit_bb = true;12724 emit_bb = true;
1250512725
12506 const prong_hint = try spa.analyzeProngRuntime(12726 const prong_hint = try spa.analyzeProngRuntime(
12507 &case_block,12727 &case_block,
12508 .special,12728 .special,
12509 special.body,12729 else_prong.body,
12510 special.capture,12730 else_prong.capture,
12511 child_block.src(.{ .switch_capture = .{12731 child_block.src(.{ .switch_capture = .{
12512 .switch_node_offset = switch_node_offset,12732 .switch_node_offset = switch_node_offset,
12513 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,12733 .case_idx = .special_else,
12514 } }),12734 } }),
12515 &.{.bool_false},12735 &.{.bool_false},
12516 .bool_false,12736 .bool_false,
12517 special.has_tag_capture,12737 else_prong.has_tag_capture,
12518 );12738 );
12519 try branch_hints.append(gpa, prong_hint);12739 try branch_hints.append(gpa, prong_hint);
1252012740
...@@ -12530,7 +12750,7 @@ fn analyzeSwitchRuntimeBlock(...@@ -12530,7 +12750,7 @@ fn analyzeSwitchRuntimeBlock(
12530 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));12750 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12531 }12751 }
12532 },12752 },
12533 else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{12753 else => return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
12534 operand_ty.fmt(pt),12754 operand_ty.fmt(pt),
12535 }),12755 }),
12536 };12756 };
...@@ -12539,7 +12759,7 @@ fn analyzeSwitchRuntimeBlock(...@@ -12539,7 +12759,7 @@ fn analyzeSwitchRuntimeBlock(
12539 case_block.error_return_trace_index = child_block.error_return_trace_index;12759 case_block.error_return_trace_index = child_block.error_return_trace_index;
1254012760
12541 if (zcu.backendSupportsFeature(.is_named_enum_value) and12761 if (zcu.backendSupportsFeature(.is_named_enum_value) and
12542 special.body.len != 0 and block.wantSafety() and12762 else_prong.body.len != 0 and block.wantSafety() and
12543 operand_ty.zigTypeTag(zcu) == .@"enum" and12763 operand_ty.zigTypeTag(zcu) == .@"enum" and
12544 (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally))12764 (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally))
12545 {12765 {
...@@ -12548,7 +12768,12 @@ fn analyzeSwitchRuntimeBlock(...@@ -12548,7 +12768,12 @@ fn analyzeSwitchRuntimeBlock(
12548 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);12768 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
12549 }12769 }
1255012770
12551 const analyze_body = if (union_originally and !special.is_inline)12771 const else_src_idx: LazySrcLoc.Offset.SwitchCaseIndex = if (else_prong_is_underscore)
12772 .special_under
12773 else
12774 .special_else;
12775
12776 const analyze_body = if (union_originally and !else_prong.is_inline)
12552 for (seen_enum_fields, 0..) |seen_field, index| {12777 for (seen_enum_fields, 0..) |seen_field, index| {
12553 if (seen_field != null) continue;12778 if (seen_field != null) continue;
12554 const union_obj = zcu.typeToUnion(maybe_union_ty).?;12779 const union_obj = zcu.typeToUnion(maybe_union_ty).?;
...@@ -12557,20 +12782,20 @@ fn analyzeSwitchRuntimeBlock(...@@ -12557,20 +12782,20 @@ fn analyzeSwitchRuntimeBlock(
12557 } else false12782 } else false
12558 else12783 else
12559 true;12784 true;
12560 const else_hint: std.builtin.BranchHint = if (special.body.len != 0 and err_set and12785 const else_hint: std.builtin.BranchHint = if (else_prong.body.len != 0 and err_set and
12561 try sema.maybeErrorUnwrap(&case_block, special.body, operand, operand_src, allow_err_code_unwrap))12786 try sema.maybeErrorUnwrap(&case_block, else_prong.body, operand, operand_src, allow_err_code_unwrap))
12562 h: {12787 h: {
12563 // nothing to do here. weight against error branch12788 // nothing to do here. weight against error branch
12564 break :h .unlikely;12789 break :h .unlikely;
12565 } else if (special.body.len != 0 and analyze_body and !special.is_inline) h: {12790 } else if (else_prong.body.len != 0 and analyze_body and !else_prong.is_inline) h: {
12566 break :h try spa.analyzeProngRuntime(12791 break :h try spa.analyzeProngRuntime(
12567 &case_block,12792 &case_block,
12568 .special,12793 .special,
12569 special.body,12794 else_prong.body,
12570 special.capture,12795 else_prong.capture,
12571 child_block.src(.{ .switch_capture = .{12796 child_block.src(.{ .switch_capture = .{
12572 .switch_node_offset = switch_node_offset,12797 .switch_node_offset = switch_node_offset,
12573 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,12798 .case_idx = else_src_idx,
12574 } }),12799 } }),
12575 undefined, // case_vals may be undefined for special prongs12800 undefined, // case_vals may be undefined for special prongs
12576 .none,12801 .none,
...@@ -12644,7 +12869,9 @@ fn resolveSwitchComptimeLoop(...@@ -12644,7 +12869,9 @@ fn resolveSwitchComptimeLoop(
12644 cond_ty: Type,12869 cond_ty: Type,
12645 init_cond_val: Value,12870 init_cond_val: Value,
12646 switch_node_offset: std.zig.Ast.Node.Offset,12871 switch_node_offset: std.zig.Ast.Node.Offset,
12647 special: SpecialProng,12872 special_members_only: ?SpecialProng,
12873 special_generic: SpecialProng,
12874 special_generic_is_under: bool,
12648 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),12875 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
12649 scalar_cases_len: u32,12876 scalar_cases_len: u32,
12650 multi_cases_len: u32,12877 multi_cases_len: u32,
...@@ -12664,7 +12891,9 @@ fn resolveSwitchComptimeLoop(...@@ -12664,7 +12891,9 @@ fn resolveSwitchComptimeLoop(
12664 cond_val,12891 cond_val,
12665 cond_ty,12892 cond_ty,
12666 switch_node_offset,12893 switch_node_offset,
12667 special,12894 special_members_only,
12895 special_generic,
12896 special_generic_is_under,
12668 case_vals,12897 case_vals,
12669 scalar_cases_len,12898 scalar_cases_len,
12670 multi_cases_len,12899 multi_cases_len,
...@@ -12712,17 +12941,20 @@ fn resolveSwitchComptime(...@@ -12712,17 +12941,20 @@ fn resolveSwitchComptime(
12712 operand_val: Value,12941 operand_val: Value,
12713 operand_ty: Type,12942 operand_ty: Type,
12714 switch_node_offset: std.zig.Ast.Node.Offset,12943 switch_node_offset: std.zig.Ast.Node.Offset,
12715 special: SpecialProng,12944 special_members_only: ?SpecialProng,
12945 special_generic: SpecialProng,
12946 special_generic_is_under: bool,
12716 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),12947 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
12717 scalar_cases_len: u32,12948 scalar_cases_len: u32,
12718 multi_cases_len: u32,12949 multi_cases_len: u32,
12719 err_set: bool,12950 err_set: bool,
12720 empty_enum: bool,12951 empty_enum: bool,
12721) CompileError!Air.Inst.Ref {12952) CompileError!Air.Inst.Ref {
12953 const zcu = sema.pt.zcu;
12722 const merges = &child_block.label.?.merges;12954 const merges = &child_block.label.?.merges;
12723 const resolved_operand_val = try sema.resolveLazyValue(operand_val);12955 const resolved_operand_val = try sema.resolveLazyValue(operand_val);
1272412956
12725 var extra_index: usize = special.end;12957 var extra_index: usize = special_generic.end;
12726 {12958 {
12727 var scalar_i: usize = 0;12959 var scalar_i: usize = 0;
12728 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {12960 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
...@@ -12823,23 +13055,45 @@ fn resolveSwitchComptime(...@@ -12823,23 +13055,45 @@ fn resolveSwitchComptime(
12823 extra_index += info.body_len;13055 extra_index += info.body_len;
12824 }13056 }
12825 }13057 }
12826 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special.body, cond_operand);13058 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special_generic.body, cond_operand);
12827 if (empty_enum) {13059 if (empty_enum) {
12828 return .void_value;13060 return .void_value;
12829 }13061 }
13062 if (special_members_only) |special| {
13063 assert(operand_ty.isNonexhaustiveEnum(zcu));
13064 if (operand_ty.enumTagFieldIndex(operand_val, zcu)) |_| {
13065 return spa.resolveProngComptime(
13066 child_block,
13067 .special,
13068 special.body,
13069 special.capture,
13070 child_block.src(.{ .switch_capture = .{
13071 .switch_node_offset = switch_node_offset,
13072 .case_idx = .special_else,
13073 } }),
13074 undefined, // case_vals may be undefined for special prongs
13075 if (special.is_inline) cond_operand else .none,
13076 special.has_tag_capture,
13077 merges,
13078 );
13079 }
13080 }
1283013081
12831 return spa.resolveProngComptime(13082 return spa.resolveProngComptime(
12832 child_block,13083 child_block,
12833 .special,13084 .special,
12834 special.body,13085 special_generic.body,
12835 special.capture,13086 special_generic.capture,
12836 child_block.src(.{ .switch_capture = .{13087 child_block.src(.{ .switch_capture = .{
12837 .switch_node_offset = switch_node_offset,13088 .switch_node_offset = switch_node_offset,
12838 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,13089 .case_idx = if (special_generic_is_under)
13090 .special_under
13091 else
13092 .special_else,
12839 } }),13093 } }),
12840 undefined, // case_vals may be undefined for special prongs13094 undefined, // case_vals may be undefined for special prongs
12841 if (special.is_inline) cond_operand else .none,13095 if (special_generic.is_inline) cond_operand else .none,
12842 special.has_tag_capture,13096 special_generic.has_tag_capture,
12843 merges,13097 merges,
12844 );13098 );
12845}13099}
src/Zcu.zig+66-37
...@@ -1679,20 +1679,37 @@ pub const SrcLoc = struct {...@@ -1679,20 +1679,37 @@ pub const SrcLoc = struct {
1679 return tree.nodeToSpan(condition);1679 return tree.nodeToSpan(condition);
1680 },1680 },
16811681
1682 .node_offset_switch_special_prong => |node_off| {1682 .node_offset_switch_else_prong => |node_off| {
1683 const tree = try src_loc.file_scope.getTree(zcu);1683 const tree = try src_loc.file_scope.getTree(zcu);
1684 const switch_node = node_off.toAbsolute(src_loc.base_node);1684 const switch_node = node_off.toAbsolute(src_loc.base_node);
1685 _, const extra_index = tree.nodeData(switch_node).node_and_extra;1685 _, const extra_index = tree.nodeData(switch_node).node_and_extra;
1686 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);1686 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
1687 for (case_nodes) |case_node| {1687 for (case_nodes) |case_node| {
1688 const case = tree.fullSwitchCase(case_node).?;1688 const case = tree.fullSwitchCase(case_node).?;
1689 const is_special = (case.ast.values.len == 0) or1689 if (case.ast.values.len == 0) {
1690 (case.ast.values.len == 1 and1690 return tree.nodeToSpan(case_node);
1691 tree.nodeTag(case.ast.values[0]) == .identifier and1691 }
1692 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_"));1692 } else unreachable;
1693 if (!is_special) continue;1693 },
16941694
1695 return tree.nodeToSpan(case_node);1695 .node_offset_switch_under_prong => |node_off| {
1696 const tree = try src_loc.file_scope.getTree(zcu);
1697 const switch_node = node_off.toAbsolute(src_loc.base_node);
1698 _, const extra_index = tree.nodeData(switch_node).node_and_extra;
1699 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
1700 for (case_nodes) |case_node| {
1701 const case = tree.fullSwitchCase(case_node).?;
1702 for (case.ast.values) |val| {
1703 if (tree.nodeTag(val) == .identifier and
1704 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_"))
1705 {
1706 return tree.tokensToSpan(
1707 tree.firstToken(case_node),
1708 tree.lastToken(case_node),
1709 tree.nodeMainToken(val),
1710 );
1711 }
1712 }
1696 } else unreachable;1713 } else unreachable;
1697 },1714 },
16981715
...@@ -1703,12 +1720,6 @@ pub const SrcLoc = struct {...@@ -1703,12 +1720,6 @@ pub const SrcLoc = struct {
1703 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);1720 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
1704 for (case_nodes) |case_node| {1721 for (case_nodes) |case_node| {
1705 const case = tree.fullSwitchCase(case_node).?;1722 const case = tree.fullSwitchCase(case_node).?;
1706 const is_special = (case.ast.values.len == 0) or
1707 (case.ast.values.len == 1 and
1708 tree.nodeTag(case.ast.values[0]) == .identifier and
1709 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_"));
1710 if (is_special) continue;
1711
1712 for (case.ast.values) |item_node| {1723 for (case.ast.values) |item_node| {
1713 if (tree.nodeTag(item_node) == .switch_range) {1724 if (tree.nodeTag(item_node) == .switch_range) {
1714 return tree.nodeToSpan(item_node);1725 return tree.nodeToSpan(item_node);
...@@ -2113,28 +2124,35 @@ pub const SrcLoc = struct {...@@ -2113,28 +2124,35 @@ pub const SrcLoc = struct {
21132124
2114 var multi_i: u32 = 0;2125 var multi_i: u32 = 0;
2115 var scalar_i: u32 = 0;2126 var scalar_i: u32 = 0;
2116 const case = for (case_nodes) |case_node| {2127 var underscore_node: Ast.Node.OptionalIndex = .none;
2128 const case = case: for (case_nodes) |case_node| {
2117 const case = tree.fullSwitchCase(case_node).?;2129 const case = tree.fullSwitchCase(case_node).?;
2118 const is_special = special: {2130 if (case.ast.values.len == 0) {
2119 if (case.ast.values.len == 0) break :special true;2131 if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special_else) {
2120 if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) == .identifier) {2132 break :case case;
2121 break :special mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_");
2122 }2133 }
2123 break :special false;2134 continue :case;
2124 };
2125 if (is_special) {
2126 if (want_case_idx.isSpecial()) {
2127 break case;
2128 }
2129 continue;
2130 }2135 }
2136 if (underscore_node == .none) for (case.ast.values) |val_node| {
2137 if (tree.nodeTag(val_node) == .identifier and
2138 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val_node)), "_"))
2139 {
2140 underscore_node = val_node.toOptional();
2141 if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special_under) {
2142 break :case case;
2143 }
2144 continue :case;
2145 }
2146 };
21312147
2132 const is_multi = case.ast.values.len != 1 or2148 const is_multi = case.ast.values.len != 1 or
2133 tree.nodeTag(case.ast.values[0]) == .switch_range;2149 tree.nodeTag(case.ast.values[0]) == .switch_range;
21342150
2135 switch (want_case_idx.kind) {2151 switch (want_case_idx.kind) {
2136 .scalar => if (!is_multi and want_case_idx.index == scalar_i) break case,2152 .scalar => if (!is_multi and want_case_idx.index == scalar_i)
2137 .multi => if (is_multi and want_case_idx.index == multi_i) break case,2153 break :case case,
2154 .multi => if (is_multi and want_case_idx.index == multi_i)
2155 break :case case,
2138 }2156 }
21392157
2140 if (is_multi) {2158 if (is_multi) {
...@@ -2148,7 +2166,10 @@ pub const SrcLoc = struct {...@@ -2148,7 +2166,10 @@ pub const SrcLoc = struct {
2148 .switch_case_item,2166 .switch_case_item,
2149 .switch_case_item_range_first,2167 .switch_case_item_range_first,
2150 .switch_case_item_range_last,2168 .switch_case_item_range_last,
2151 => |x| x.item_idx,2169 => |x| item_idx: {
2170 assert(want_case_idx != LazySrcLoc.Offset.SwitchCaseIndex.special_else);
2171 break :item_idx x.item_idx;
2172 },
2152 .switch_capture, .switch_tag_capture => {2173 .switch_capture, .switch_tag_capture => {
2153 const start = switch (src_loc.lazy) {2174 const start = switch (src_loc.lazy) {
2154 .switch_capture => case.payload_token.?,2175 .switch_capture => case.payload_token.?,
...@@ -2173,7 +2194,11 @@ pub const SrcLoc = struct {...@@ -2173,7 +2194,11 @@ pub const SrcLoc = struct {
2173 .single => {2194 .single => {
2174 var item_i: u32 = 0;2195 var item_i: u32 = 0;
2175 for (case.ast.values) |item_node| {2196 for (case.ast.values) |item_node| {
2176 if (tree.nodeTag(item_node) == .switch_range) continue;2197 if (item_node.toOptional() == underscore_node or
2198 tree.nodeTag(item_node) == .switch_range)
2199 {
2200 continue;
2201 }
2177 if (item_i != want_item.index) {2202 if (item_i != want_item.index) {
2178 item_i += 1;2203 item_i += 1;
2179 continue;2204 continue;
...@@ -2184,7 +2209,9 @@ pub const SrcLoc = struct {...@@ -2184,7 +2209,9 @@ pub const SrcLoc = struct {
2184 .range => {2209 .range => {
2185 var range_i: u32 = 0;2210 var range_i: u32 = 0;
2186 for (case.ast.values) |item_node| {2211 for (case.ast.values) |item_node| {
2187 if (tree.nodeTag(item_node) != .switch_range) continue;2212 if (tree.nodeTag(item_node) != .switch_range) {
2213 continue;
2214 }
2188 if (range_i != want_item.index) {2215 if (range_i != want_item.index) {
2189 range_i += 1;2216 range_i += 1;
2190 continue;2217 continue;
...@@ -2363,10 +2390,14 @@ pub const LazySrcLoc = struct {...@@ -2363,10 +2390,14 @@ pub const LazySrcLoc = struct {
2363 /// by taking this AST node index offset from the containing base node,2390 /// by taking this AST node index offset from the containing base node,
2364 /// which points to a switch expression AST node. Next, navigate to the operand.2391 /// which points to a switch expression AST node. Next, navigate to the operand.
2365 node_offset_switch_operand: Ast.Node.Offset,2392 node_offset_switch_operand: Ast.Node.Offset,
2366 /// The source location points to the else/`_` prong of a switch expression, found2393 /// The source location points to the else prong of a switch expression, found
2394 /// by taking this AST node index offset from the containing base node,
2395 /// which points to a switch expression AST node. Next, navigate to the else prong.
2396 node_offset_switch_else_prong: Ast.Node.Offset,
2397 /// The source location points to the `_` prong of a switch expression, found
2367 /// by taking this AST node index offset from the containing base node,2398 /// by taking this AST node index offset from the containing base node,
2368 /// which points to a switch expression AST node. Next, navigate to the else/`_` prong.2399 /// which points to a switch expression AST node. Next, navigate to the `_` prong.
2369 node_offset_switch_special_prong: Ast.Node.Offset,2400 node_offset_switch_under_prong: Ast.Node.Offset,
2370 /// The source location points to all the ranges of a switch expression, found2401 /// The source location points to all the ranges of a switch expression, found
2371 /// by taking this AST node index offset from the containing base node,2402 /// by taking this AST node index offset from the containing base node,
2372 /// which points to a switch expression AST node. Next, navigate to any of the2403 /// which points to a switch expression AST node. Next, navigate to any of the
...@@ -2562,10 +2593,8 @@ pub const LazySrcLoc = struct {...@@ -2562,10 +2593,8 @@ pub const LazySrcLoc = struct {
2562 kind: enum(u1) { scalar, multi },2593 kind: enum(u1) { scalar, multi },
2563 index: u31,2594 index: u31,
25642595
2565 pub const special: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32)));2596 pub const special_else: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32)));
2566 pub fn isSpecial(idx: SwitchCaseIndex) bool {2597 pub const special_under: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32) - 1));
2567 return @as(u32, @bitCast(idx)) == @as(u32, @bitCast(special));
2568 }
2569 };2598 };
25702599
2571 pub const SwitchItemIndex = packed struct(u32) {2600 pub const SwitchItemIndex = packed struct(u32) {
src/print_zir.zig+65-14
...@@ -2087,15 +2087,10 @@ const Writer = struct {...@@ -2087,15 +2087,10 @@ const Writer = struct {
20872087
2088 self.indent += 2;2088 self.indent += 2;
20892089
2090 else_prong: {2090 const special_prongs = extra.data.bits.special_prongs;
2091 const special_prong = extra.data.bits.specialProng();
2092 const prong_name = switch (special_prong) {
2093 .@"else" => "else",
2094 .under => "_",
2095 else => break :else_prong,
2096 };
20972091
2098 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));2092 if (special_prongs.hasElse()) {
2093 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
2099 const capture_text = switch (info.capture) {2094 const capture_text = switch (info.capture) {
2100 .none => "",2095 .none => "",
2101 .by_val => "by_val ",2096 .by_val => "by_val ",
...@@ -2108,7 +2103,63 @@ const Writer = struct {...@@ -2108,7 +2103,63 @@ const Writer = struct {
21082103
2109 try stream.writeAll(",\n");2104 try stream.writeAll(",\n");
2110 try stream.splatByteAll(' ', self.indent);2105 try stream.splatByteAll(' ', self.indent);
2111 try stream.print("{s}{s}{s} => ", .{ capture_text, inline_text, prong_name });2106 try stream.print("{s}{s}else => ", .{ capture_text, inline_text });
2107 try self.writeBracedBody(stream, body);
2108 }
2109
2110 if (special_prongs.hasUnder()) {
2111 var single_item_ref: Zir.Inst.Ref = .none;
2112 var items_len: u32 = 0;
2113 var ranges_len: u32 = 0;
2114 if (special_prongs.hasOneAdditionalItem()) {
2115 single_item_ref = @enumFromInt(self.code.extra[extra_index]);
2116 extra_index += 1;
2117 } else if (special_prongs.hasManyAdditionalItems()) {
2118 items_len = self.code.extra[extra_index];
2119 extra_index += 1;
2120 ranges_len = self.code.extra[extra_index];
2121 extra_index += 1;
2122 }
2123 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
2124 extra_index += 1;
2125 const items = self.code.refSlice(extra_index, items_len);
2126 extra_index += items_len;
2127
2128 try stream.writeAll(",\n");
2129 try stream.splatByteAll(' ', self.indent);
2130 switch (info.capture) {
2131 .none => {},
2132 .by_val => try stream.writeAll("by_val "),
2133 .by_ref => try stream.writeAll("by_ref "),
2134 }
2135 if (info.is_inline) try stream.writeAll("inline ");
2136
2137 try stream.writeAll("_");
2138 if (single_item_ref != .none) {
2139 try stream.writeAll(", ");
2140 try self.writeInstRef(stream, single_item_ref);
2141 }
2142 for (items) |item_ref| {
2143 try stream.writeAll(", ");
2144 try self.writeInstRef(stream, item_ref);
2145 }
2146
2147 var range_i: usize = 0;
2148 while (range_i < ranges_len) : (range_i += 1) {
2149 const item_first: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2150 extra_index += 1;
2151 const item_last: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2152 extra_index += 1;
2153
2154 try stream.writeAll(", ");
2155 try self.writeInstRef(stream, item_first);
2156 try stream.writeAll("...");
2157 try self.writeInstRef(stream, item_last);
2158 }
2159
2160 const body = self.code.bodySlice(extra_index, info.body_len);
2161 extra_index += info.body_len;
2162 try stream.writeAll(" => ");
2112 try self.writeBracedBody(stream, body);2163 try self.writeBracedBody(stream, body);
2113 }2164 }
21142165
...@@ -2116,9 +2167,9 @@ const Writer = struct {...@@ -2116,9 +2167,9 @@ const Writer = struct {
2116 const scalar_cases_len = extra.data.bits.scalar_cases_len;2167 const scalar_cases_len = extra.data.bits.scalar_cases_len;
2117 var scalar_i: usize = 0;2168 var scalar_i: usize = 0;
2118 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {2169 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2119 const item_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));2170 const item_ref: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2120 extra_index += 1;2171 extra_index += 1;
2121 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));2172 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
2122 extra_index += 1;2173 extra_index += 1;
2123 const body = self.code.bodySlice(extra_index, info.body_len);2174 const body = self.code.bodySlice(extra_index, info.body_len);
2124 extra_index += info.body_len;2175 extra_index += info.body_len;
...@@ -2143,7 +2194,7 @@ const Writer = struct {...@@ -2143,7 +2194,7 @@ const Writer = struct {
2143 extra_index += 1;2194 extra_index += 1;
2144 const ranges_len = self.code.extra[extra_index];2195 const ranges_len = self.code.extra[extra_index];
2145 extra_index += 1;2196 extra_index += 1;
2146 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));2197 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
2147 extra_index += 1;2198 extra_index += 1;
2148 const items = self.code.refSlice(extra_index, items_len);2199 const items = self.code.refSlice(extra_index, items_len);
2149 extra_index += items_len;2200 extra_index += items_len;
...@@ -2164,9 +2215,9 @@ const Writer = struct {...@@ -2164,9 +2215,9 @@ const Writer = struct {
21642215
2165 var range_i: usize = 0;2216 var range_i: usize = 0;
2166 while (range_i < ranges_len) : (range_i += 1) {2217 while (range_i < ranges_len) : (range_i += 1) {
2167 const item_first = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));2218 const item_first: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2168 extra_index += 1;2219 extra_index += 1;
2169 const item_last = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));2220 const item_last: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2170 extra_index += 1;2221 extra_index += 1;
21712222
2172 if (range_i != 0 or items.len != 0) {2223 if (range_i != 0 or items.len != 0) {
test/behavior/switch.zig+47
...@@ -1073,3 +1073,50 @@ test "switch on 8-bit mod result" {...@@ -1073,3 +1073,50 @@ test "switch on 8-bit mod result" {
1073 else => unreachable,1073 else => unreachable,
1074 }1074 }
1075}1075}
1076
1077test "switch on non-exhaustive enum" {
1078 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
1079
1080 const E = enum(u4) {
1081 a,
1082 b,
1083 c,
1084 _,
1085
1086 fn doTheTest(e: @This()) !void {
1087 switch (e) {
1088 .a, .b => {},
1089 else => return error.TestFailed,
1090 }
1091 switch (e) {
1092 .a, .b => {},
1093 .c => return error.TestFailed,
1094 _ => return error.TestFailed,
1095 }
1096 switch (e) {
1097 .a, .b => {},
1098 .c, _ => return error.TestFailed,
1099 }
1100 switch (e) {
1101 .a => {},
1102 .b, .c, _ => return error.TestFailed,
1103 }
1104 switch (e) {
1105 .b => return error.TestFailed,
1106 else => {},
1107 _ => return error.TestFailed,
1108 }
1109 switch (e) {
1110 else => {},
1111 _ => return error.TestFailed,
1112 }
1113 switch (e) {
1114 inline else => {},
1115 _ => return error.TestFailed,
1116 }
1117 }
1118 };
1119
1120 try E.doTheTest(.a);
1121 try comptime E.doTheTest(.a);
1122}
test/behavior/switch_loop.zig+24
...@@ -249,3 +249,27 @@ test "switch loop on larger than pointer integer" {...@@ -249,3 +249,27 @@ test "switch loop on larger than pointer integer" {
249 }249 }
250 try expect(entry == 3);250 try expect(entry == 3);
251}251}
252
253test "switch loop on non-exhaustive enum" {
254 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
255 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
256 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
257 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
258
259 const S = struct {
260 const E = enum(u8) { a, b, c, _ };
261
262 fn doTheTest() !void {
263 var start: E = undefined;
264 start = .a;
265 const result: u32 = s: switch (start) {
266 .a => continue :s .c,
267 else => continue :s @enumFromInt(123),
268 .b, _ => |x| break :s @intFromEnum(x),
269 };
270 try expect(result == 123);
271 }
272 };
273 try S.doTheTest();
274 try comptime S.doTheTest();
275}
test/cases/compile_errors/switch_expression-non_exhaustive_absorbing.zig created+31
...@@ -0,0 +1,31 @@
1const E = enum(u8) {
2 a,
3 b,
4 _,
5};
6const U = union(E) {
7 a: i32,
8 b: u32,
9};
10pub export fn entry1() void {
11 const e: E = .b;
12 switch (e) { // error: switch not handling the tag `b`
13 .a, _ => {},
14 }
15}
16pub export fn entry2() void {
17 const u = U{ .a = 2 };
18 switch (u) { // error: `_` prong not allowed when switching on tagged union
19 .a => {},
20 .b, _ => {},
21 }
22}
23
24// error
25//
26// :12:5: error: switch must handle all possibilities
27// :3:5: note: unhandled enumeration value: 'b'
28// :1:11: note: enum 'tmp.E' declared here
29// :18:5: error: '_' prong only allowed when switching on non-exhaustive enums
30// :20:13: note: '_' prong here
31// :18:5: note: consider using 'else'
test/cases/compile_errors/switch_expression-non_exhaustive_inline.zig created+25
...@@ -0,0 +1,25 @@
1const E = enum(u8) {
2 a,
3 b,
4 _,
5};
6
7export fn f(e: E) void {
8 switch (e) {
9 .a => {},
10 inline _ => {},
11 }
12}
13
14export fn g(e: E) void {
15 switch (e) {
16 .a => {},
17 else => {},
18 inline _ => {},
19 }
20}
21
22// error
23//
24// :10:16: error: cannot inline '_' prong
25// :18:16: error: cannot inline '_' prong
test/cases/compile_errors/switch_expression-non_exhaustive_unreachable_else.zig created+16
...@@ -0,0 +1,16 @@
1const E = enum(u8) {
2 a,
3 b,
4 _,
5};
6
7export fn f(e: E) void {
8 switch (e) {
9 .a, .b, _ => {},
10 else => {},
11 }
12}
13
14// error
15//
16// :10:14: error: unreachable else prong; all explicit cases already handled
test/cases/compile_errors/switching_with_exhaustive_enum_has___prong_.zig+1-1
...@@ -16,5 +16,5 @@ pub export fn entry() void {...@@ -16,5 +16,5 @@ pub export fn entry() void {
16// target=native16// target=native
17//17//
18// :7:5: error: '_' prong only allowed when switching on non-exhaustive enums18// :7:5: error: '_' prong only allowed when switching on non-exhaustive enums
19// :10:11: note: '_' prong here19// :10:9: note: '_' prong here
20// :7:5: note: consider using 'else'20// :7:5: note: consider using 'else'
test/cases/compile_errors/switching_with_non-exhaustive_enums.zig+2-2
...@@ -37,7 +37,7 @@ pub export fn entry3() void {...@@ -37,7 +37,7 @@ pub export fn entry3() void {
37// :12:5: error: switch must handle all possibilities37// :12:5: error: switch must handle all possibilities
38// :3:5: note: unhandled enumeration value: 'b'38// :3:5: note: unhandled enumeration value: 'b'
39// :1:11: note: enum 'tmp.E' declared here39// :1:11: note: enum 'tmp.E' declared here
40// :19:5: error: switch on non-exhaustive enum must include 'else' or '_' prong40// :19:5: error: switch on non-exhaustive enum must include 'else' or '_' prong or both
41// :26:5: error: '_' prong only allowed when switching on non-exhaustive enums41// :26:5: error: '_' prong only allowed when switching on non-exhaustive enums
42// :29:11: note: '_' prong here42// :29:9: note: '_' prong here
43// :26:5: note: consider using 'else'43// :26:5: note: consider using 'else'