authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2025-07-10 01:58:02+02:00
committergravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2025-08-07 13:58:47+02:00
logba549a7d67d0268cdbd4b23a5b7371de4f2d8d33
tree2ee5ed3a9546923fa41abeac193634f88f944d82
parent1d9b1c021273037efea6623e669800ad538c8075

Add support for both '_' and 'else' prongs at the same time in switch statements

If both are used, 'else' handles named members and '_' handles unnamed members. In this case the 'else' prong will be unrolled to an explicit case containing all remaining named values.

11 files changed, 769 insertions(+), 363 deletions(-)

lib/std/zig/Ast.zig-18
...@@ -2877,24 +2877,6 @@ pub const full = struct {...@@ -2877,24 +2877,6 @@ pub const full = struct {
2877 arrow_token: TokenIndex,2877 arrow_token: TokenIndex,
2878 target_expr: Node.Index,2878 target_expr: Node.Index,
2879 };2879 };
2880
2881 /// Returns:
2882 /// `null` if case is not special
2883 /// `.none` if case is else prong
2884 /// Index of underscore otherwise
2885 pub fn isSpecial(case: *const SwitchCase, tree: *const Ast) ?Node.OptionalIndex {
2886 if (case.ast.values.len == 0) {
2887 return .none;
2888 }
2889 for (case.ast.values) |val| {
2890 if (tree.nodeTag(val) == .identifier and
2891 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_"))
2892 {
2893 return val.toOptional();
2894 }
2895 }
2896 return null;
2897 }
2898 };2880 };
28992881
2900 pub const Asm = struct {2882 pub const Asm = struct {
lib/std/zig/AstGen.zig+77-72
...@@ -7662,11 +7662,12 @@ fn switchExpr(...@@ -7662,11 +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;
7668 var underscore_src: ?Ast.TokenIndex = null;7667 var underscore_case_node: Ast.Node.OptionalIndex = .none;
7669 var underscore_node: Ast.Node.OptionalIndex = .none;7668 var underscore_node: Ast.Node.OptionalIndex = .none;
7669 var underscore_src: ?Ast.TokenIndex = null;
7670 var underscore_additional_items: Zir.SpecialProngs.AdditionalItems = .none;
7670 for (case_nodes) |case_node| {7671 for (case_nodes) |case_node| {
7671 const case = tree.fullSwitchCase(case_node).?;7672 const case = tree.fullSwitchCase(case_node).?;
7672 if (case.payload_token) |payload_token| {7673 if (case.payload_token) |payload_token| {
...@@ -7687,6 +7688,7 @@ fn switchExpr(...@@ -7687,6 +7688,7 @@ fn switchExpr(
7687 any_non_inline_capture = true;7688 any_non_inline_capture = true;
7688 }7689 }
7689 }7690 }
7691
7690 // Check for else prong.7692 // Check for else prong.
7691 if (case.ast.values.len == 0) {7693 if (case.ast.values.len == 0) {
7692 const case_src = case.ast.arrow_token - 1;7694 const case_src = case.ast.arrow_token - 1;
...@@ -7703,40 +7705,21 @@ fn switchExpr(...@@ -7703,40 +7705,21 @@ fn switchExpr(
7703 ),7705 ),
7704 },7706 },
7705 );7707 );
7706 } else if (underscore_src) |some_underscore| {
7707 return astgen.failNodeNotes(
7708 node,
7709 "else and '_' prong in switch expression",
7710 .{},
7711 &[_]u32{
7712 try astgen.errNoteTok(
7713 case_src,
7714 "else prong here",
7715 .{},
7716 ),
7717 try astgen.errNoteTok(
7718 some_underscore,
7719 "'_' prong here",
7720 .{},
7721 ),
7722 },
7723 );
7724 }7708 }
7725 special_node = case_node.toOptional();7709 else_case_node = case_node.toOptional();
7726 special_prong = .@"else";
7727 else_src = case_src;7710 else_src = case_src;
7728 continue;7711 continue;
7729 }7712 }
77307713
7731 // Check for '_' prong.7714 // Check for '_' prong.
7732 var found_underscore = false;7715 var case_has_underscore = false;
7733 for (case.ast.values) |val| {7716 for (case.ast.values) |val| {
7734 switch (tree.nodeTag(val)) {7717 switch (tree.nodeTag(val)) {
7735 .identifier => if (mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_")) {7718 .identifier => if (mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_")) {
7736 const case_src = case.ast.arrow_token - 1;7719 const val_src = tree.nodeMainToken(val);
7737 if (underscore_src) |src| {7720 if (underscore_src) |src| {
7738 return astgen.failTokNotes(7721 return astgen.failTokNotes(
7739 case_src,7722 val_src,
7740 "multiple '_' prongs in switch expression",7723 "multiple '_' prongs in switch expression",
7741 .{},7724 .{},
7742 &[_]u32{7725 &[_]u32{
...@@ -7747,39 +7730,26 @@ fn switchExpr(...@@ -7747,39 +7730,26 @@ fn switchExpr(
7747 ),7730 ),
7748 },7731 },
7749 );7732 );
7750 } else if (else_src) |some_else| {
7751 return astgen.failNodeNotes(
7752 node,
7753 "else and '_' prong in switch expression",
7754 .{},
7755 &[_]u32{
7756 try astgen.errNoteTok(
7757 some_else,
7758 "else prong here",
7759 .{},
7760 ),
7761 try astgen.errNoteTok(
7762 case_src,
7763 "'_' prong here",
7764 .{},
7765 ),
7766 },
7767 );
7768 }7733 }
7769 if (case.inline_token != null) {7734 if (case.inline_token != null) {
7770 return astgen.failTok(case_src, "cannot inline '_' prong", .{});7735 return astgen.failTok(val_src, "cannot inline '_' prong", .{});
7771 }7736 }
7772 special_node = case_node.toOptional();7737 underscore_case_node = case_node.toOptional();
7773 special_prong = if (case.ast.values.len == 1) .under else .absorbing_under;7738 underscore_src = val_src;
7774 underscore_src = case_src;
7775 underscore_node = val.toOptional();7739 underscore_node = val.toOptional();
7776 found_underscore = true;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;
7777 },7747 },
7778 .string_literal => return astgen.failNode(val, "cannot switch on strings", .{}),7748 .string_literal => return astgen.failNode(val, "cannot switch on strings", .{}),
7779 else => {},7749 else => {},
7780 }7750 }
7781 }7751 }
7782 if (found_underscore) continue;7752 if (case_has_underscore) continue;
77837753
7784 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) {
7785 scalar_cases_len += 1;7755 scalar_cases_len += 1;
...@@ -7791,6 +7761,14 @@ fn switchExpr(...@@ -7791,6 +7761,14 @@ fn switchExpr(
7791 }7761 }
7792 }7762 }
77937763
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
7794 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 };
77957773
7796 astgen.advanceSourceCursorToNode(operand_node);7774 astgen.advanceSourceCursorToNode(operand_node);
...@@ -7811,7 +7789,9 @@ fn switchExpr(...@@ -7811,7 +7789,9 @@ fn switchExpr(
7811 const payloads = &astgen.scratch;7789 const payloads = &astgen.scratch;
7812 const scratch_top = astgen.scratch.items.len;7790 const scratch_top = astgen.scratch.items.len;
7813 const case_table_start = scratch_top;7791 const case_table_start = scratch_top;
7814 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);
7815 const multi_case_table = scalar_case_table + scalar_cases_len;7795 const multi_case_table = scalar_case_table + scalar_cases_len;
7816 const case_table_end = multi_case_table + multi_cases_len;7796 const case_table_end = multi_case_table + multi_cases_len;
7817 try astgen.scratch.resize(gpa, case_table_end);7797 try astgen.scratch.resize(gpa, case_table_end);
...@@ -7943,9 +7923,19 @@ fn switchExpr(...@@ -7943,9 +7923,19 @@ fn switchExpr(
79437923
7944 const header_index: u32 = @intCast(payloads.items.len);7924 const header_index: u32 = @intCast(payloads.items.len);
7945 const body_len_index = if (is_multi_case) blk: {7925 const body_len_index = if (is_multi_case) blk: {
7946 if (case_node.toOptional() == special_node) {7926 if (case_node.toOptional() == underscore_case_node) {
7947 assert(special_prong == .absorbing_under);7927 payloads.items[under_case_index] = header_index;
7948 payloads.items[case_table_start] = 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 }
7949 } else {7939 } else {
7950 payloads.items[multi_case_table + multi_case_index] = header_index;7940 payloads.items[multi_case_table + multi_case_index] = header_index;
7951 multi_case_index += 1;7941 multi_case_index += 1;
...@@ -7985,9 +7975,13 @@ fn switchExpr(...@@ -7985,9 +7975,13 @@ fn switchExpr(
7985 payloads.items[header_index] = items_len;7975 payloads.items[header_index] = items_len;
7986 payloads.items[header_index + 1] = ranges_len;7976 payloads.items[header_index + 1] = ranges_len;
7987 break :blk header_index + 2;7977 break :blk header_index + 2;
7988 } else if (case_node.toOptional() == special_node) blk: {7978 } else if (case_node.toOptional() == else_case_node) blk: {
7989 assert(special_prong != .absorbing_under);7979 payloads.items[else_case_index] = header_index;
7990 payloads.items[case_table_start] = 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;
7991 try payloads.resize(gpa, header_index + 1); // body_len7985 try payloads.resize(gpa, header_index + 1); // body_len
7992 break :blk header_index;7986 break :blk header_index;
7993 } else blk: {7987 } else blk: {
...@@ -8048,7 +8042,7 @@ fn switchExpr(...@@ -8048,7 +8042,7 @@ fn switchExpr(
8048 .operand = raw_operand,8042 .operand = raw_operand,
8049 .bits = Zir.Inst.SwitchBlock.Bits{8043 .bits = Zir.Inst.SwitchBlock.Bits{
8050 .has_multi_cases = multi_cases_len != 0,8044 .has_multi_cases = multi_cases_len != 0,
8051 .special_prong = special_prong,8045 .special_prongs = special_prongs,
8052 .any_has_tag_capture = any_has_tag_capture,8046 .any_has_tag_capture = any_has_tag_capture,
8053 .any_non_inline_capture = any_non_inline_capture,8047 .any_non_inline_capture = any_non_inline_capture,
8054 .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,
...@@ -8067,29 +8061,40 @@ fn switchExpr(...@@ -8067,29 +8061,40 @@ fn switchExpr(
8067 const zir_datas = astgen.instructions.items(.data);8061 const zir_datas = astgen.instructions.items(.data);
8068 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;8062 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
80698063
8070 var normal_case_table_start = case_table_start;8064 if (has_else) {
8071 if (special_prong != .none) {8065 const start_index = payloads.items[else_case_index];
8072 normal_case_table_start += 1;8066 var end_index = start_index + 1;
80738067 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[start_index]);
8074 const start_index = payloads.items[case_table_start];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];
8075 var body_len_index = start_index;8073 var body_len_index = start_index;
8076 var end_index = start_index;8074 var end_index = start_index;
8077 if (special_prong == .absorbing_under) {8075 switch (underscore_additional_items) {
8078 body_len_index += 2;8076 .none => {
8079 const items_len = payloads.items[start_index];8077 end_index += 1;
8080 const ranges_len = payloads.items[start_index + 1];8078 },
8081 end_index += 3 + items_len + 2 * ranges_len;8079 .one => {
8082 } else {8080 body_len_index += 1;
8083 end_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 },
8084 }8089 }
8085 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);8090 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
8086 end_index += prong_info.body_len;8091 end_index += prong_info.body_len;
8087 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);8092 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
8088 }8093 }
8089 for (payloads.items[normal_case_table_start..case_table_end], 0..) |start_index, i| {8094 for (payloads.items[scalar_case_table..case_table_end], 0..) |start_index, i| {
8090 var body_len_index = start_index;8095 var body_len_index = start_index;
8091 var end_index = start_index;8096 var end_index = start_index;
8092 const table_index = normal_case_table_start + i;8097 const table_index = scalar_case_table + i;
8093 if (table_index < multi_case_table) {8098 if (table_index < multi_case_table) {
8094 body_len_index += 1;8099 body_len_index += 1;
8095 end_index += 2;8100 end_index += 2;
lib/std/zig/Zir.zig+101-34
...@@ -3226,9 +3226,14 @@ pub const Inst = struct {...@@ -3226,9 +3226,14 @@ 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 special_prong != .none3229 /// 2. else_body { // If special_prong.hasElse() is set.
3230 /// items_len: u32, // If special_prong == .absorbing_under3230 /// info: ProngInfo,
3231 /// ranges_len: u32, // If special_prong == .absorbing_under3231 /// body member Index for every info.body_len
3232 /// }
3233 /// 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.
3232 /// info: ProngInfo,3237 /// info: ProngInfo,
3233 /// item: Ref, // for every items_len3238 /// item: Ref, // for every items_len
3234 /// ranges: { // for every ranges_len3239 /// ranges: { // for every ranges_len
...@@ -3237,12 +3242,12 @@ pub const Inst = struct {...@@ -3237,12 +3242,12 @@ pub const Inst = struct {
3237 /// }3242 /// }
3238 /// body member Index for every info.body_len3243 /// body member Index for every info.body_len
3239 /// }3244 /// }
3240 /// 3. scalar_cases: { // for every scalar_cases_len3245 /// 4. scalar_cases: { // for every scalar_cases_len
3241 /// item: Ref,3246 /// item: Ref,
3242 /// info: ProngInfo,3247 /// info: ProngInfo,
3243 /// body member Index for every info.body_len3248 /// body member Index for every info.body_len
3244 /// }3249 /// }
3245 /// 4. multi_cases: { // for every multi_cases_len3250 /// 5. multi_cases: { // for every multi_cases_len
3246 /// items_len: u32,3251 /// items_len: u32,
3247 /// ranges_len: u32,3252 /// ranges_len: u32,
3248 /// info: ProngInfo,3253 /// info: ProngInfo,
...@@ -3283,16 +3288,17 @@ pub const Inst = struct {...@@ -3283,16 +3288,17 @@ pub const Inst = struct {
3283 /// If true, one or more prongs have multiple items.3288 /// If true, one or more prongs have multiple items.
3284 has_multi_cases: bool,3289 has_multi_cases: bool,
3285 /// Information about the special prong.3290 /// Information about the special prong.
3286 special_prong: SpecialProng,3291 special_prongs: SpecialProngs,
3287 /// If true, at least one prong has an inline tag capture.3292 /// If true, at least one prong has an inline tag capture.
3288 any_has_tag_capture: bool,3293 any_has_tag_capture: bool,
3289 /// If true, at least one prong has a capture which may not3294 /// If true, at least one prong has a capture which may not
3290 /// be comptime-known via `inline`.3295 /// be comptime-known via `inline`.
3291 any_non_inline_capture: bool,3296 any_non_inline_capture: bool,
3297 /// If true, at least one prong contains a `continue`.
3292 has_continue: bool,3298 has_continue: bool,
3293 scalar_cases_len: ScalarCasesLen,3299 scalar_cases_len: ScalarCasesLen,
32943300
3295 pub const ScalarCasesLen = u26;3301 pub const ScalarCasesLen = u25;
3296 };3302 };
32973303
3298 pub const MultiProng = struct {3304 pub const MultiProng = struct {
...@@ -3868,17 +3874,67 @@ pub const Inst = struct {...@@ -3868,17 +3874,67 @@ pub const Inst = struct {
3868 };3874 };
3869};3875};
38703876
3871pub const SpecialProng = enum(u2) {3877pub const SpecialProngs = enum(u3) {
3872 none,3878 none = 0b000,
3873 /// Simple else prong.3879 /// Simple `else` prong.
3874 /// `else => {}`3880 /// `else => {},`
3875 @"else",3881 @"else" = 0b001,
3876 /// Simple '_' prong.3882 /// Simple `_` prong.
3877 /// `_ => {}`3883 /// `_ => {},`
3878 under,3884 under = 0b010,
3879 /// '_' prong with additional items.3885 /// Both an `else` and a `_` prong.
3880 /// `a, _, b => {}`3886 /// `else => {},`
3881 absorbing_under,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 }
3882};3938};
38833939
3884pub const DeclIterator = struct {3940pub const DeclIterator = struct {
...@@ -4723,7 +4779,7 @@ fn findTrackableSwitch(...@@ -4723,7 +4779,7 @@ fn findTrackableSwitch(
4723 }4779 }
47244780
4725 const has_special = switch (kind) {4781 const has_special = switch (kind) {
4726 .normal => extra.data.bits.special_prong != .none,4782 .normal => extra.data.bits.special_prongs != .none,
4727 .err_union => has_special: {4783 .err_union => has_special: {
4728 // Handle `non_err_body` first.4784 // Handle `non_err_body` first.
4729 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);4785 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
...@@ -4738,29 +4794,40 @@ fn findTrackableSwitch(...@@ -4738,29 +4794,40 @@ fn findTrackableSwitch(
4738 };4794 };
47394795
4740 if (has_special) {4796 if (has_special) {
4741 if (kind == .normal) {4797 const has_else = if (kind == .normal)
4742 if (extra.data.bits.special_prong == .absorbing_under) {4798 extra.data.bits.special_prongs.hasElse()
4743 const items_len = zir.extra[extra_index];4799 else
4744 extra_index += 1;4800 true;
4745 const ranges_len = zir.extra[extra_index];4801 if (has_else) {
4746 extra_index += 1;4802 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
4747 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);4803 extra_index += 1;
4748 extra_index += 1;4804 const body = zir.bodySlice(extra_index, prong_info.body_len);
4805 extra_index += body.len;
47494806
4750 extra_index += items_len + ranges_len * 2;4807 try zir.findTrackableBody(gpa, contents, defers, body);
4808 }
4809 if (kind == .normal) {
4810 const special_prongs = extra.data.bits.special_prongs;
47514811
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;
4752 const body = zir.bodySlice(extra_index, prong_info.body_len);4825 const body = zir.bodySlice(extra_index, prong_info.body_len);
4753 extra_index += body.len;4826 extra_index += body.len;
47544827
4755 try zir.findTrackableBody(gpa, contents, defers, body);4828 try zir.findTrackableBody(gpa, contents, defers, body);
4756 }4829 }
4757 }4830 }
4758 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
4759 extra_index += 1;
4760 const body = zir.bodySlice(extra_index, prong_info.body_len);
4761 extra_index += body.len;
4762
4763 try zir.findTrackableBody(gpa, contents, defers, body);
4764 }4831 }
47654832
4766 {4833 {
src/Sema.zig+389-175
...@@ -10928,7 +10928,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -10928,7 +10928,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
10928 const switch_src = block.nodeOffset(inst_data.src_node);10928 const switch_src = block.nodeOffset(inst_data.src_node);
10929 const switch_src_node_offset = inst_data.src_node;10929 const switch_src_node_offset = inst_data.src_node;
10930 const switch_operand_src = block.src(.{ .node_offset_switch_operand = switch_src_node_offset });10930 const switch_operand_src = block.src(.{ .node_offset_switch_operand = switch_src_node_offset });
10931 const else_prong_src = block.src(.{ .node_offset_switch_special_prong = switch_src_node_offset });10931 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = switch_src_node_offset });
10932 const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);10932 const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);
10933 const main_operand_src = block.src(.{ .node_offset_if_cond = extra.data.main_src_node_offset });10933 const main_operand_src = block.src(.{ .node_offset_if_cond = extra.data.main_src_node_offset });
10934 const main_src = block.src(.{ .node_offset_main_token = extra.data.main_src_node_offset });10934 const main_src = block.src(.{ .node_offset_main_token = extra.data.main_src_node_offset });
...@@ -11122,6 +11122,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11122,6 +11122,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
11122 err_val,11122 err_val,
11123 operand_err_set_ty,11123 operand_err_set_ty,
11124 switch_src_node_offset,11124 switch_src_node_offset,
11125 null,
11125 .{11126 .{
11126 .body = else_case.body,11127 .body = else_case.body,
11127 .end = else_case.end,11128 .end = else_case.end,
...@@ -11129,6 +11130,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11129,6 +11130,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
11129 .is_inline = else_case.is_inline,11130 .is_inline = else_case.is_inline,
11130 .has_tag_capture = false,11131 .has_tag_capture = false,
11131 },11132 },
11133 false,
11132 case_vals,11134 case_vals,
11133 scalar_cases_len,11135 scalar_cases_len,
11134 multi_cases_len,11136 multi_cases_len,
...@@ -11200,6 +11202,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11200,6 +11202,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
11200 true,11202 true,
11201 switch_src_node_offset,11203 switch_src_node_offset,
11202 else_prong_src,11204 else_prong_src,
11205 false,
11203 undefined,11206 undefined,
11204 seen_errors,11207 seen_errors,
11205 undefined,11208 undefined,
...@@ -11207,6 +11210,10 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11207,6 +11210,10 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
11207 undefined,11210 undefined,
11208 cond_dbg_node_index,11211 cond_dbg_node_index,
11209 true,11212 true,
11213 null,
11214 undefined,
11215 &.{},
11216 &.{},
11210 );11217 );
1121111218
11212 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +11219 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
...@@ -11243,12 +11250,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11243,12 +11250,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1124311250
11244 const pt = sema.pt;11251 const pt = sema.pt;
11245 const zcu = pt.zcu;11252 const zcu = pt.zcu;
11253 const ip = &zcu.intern_pool;
11246 const gpa = sema.gpa;11254 const gpa = sema.gpa;
11247 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;11255 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
11248 const src = block.nodeOffset(inst_data.src_node);11256 const src = block.nodeOffset(inst_data.src_node);
11249 const src_node_offset = inst_data.src_node;11257 const src_node_offset = inst_data.src_node;
11250 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });11258 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
11251 const special_prong_src = block.src(.{ .node_offset_switch_special_prong = src_node_offset });11259 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
11260 const under_prong_src = block.src(.{ .node_offset_switch_under_prong = src_node_offset });
11252 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);11261 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
1125311262
11254 const operand: SwitchProngAnalysis.Operand, const raw_operand_ty: Type = op: {11263 const operand: SwitchProngAnalysis.Operand, const raw_operand_ty: Type = op: {
...@@ -11335,50 +11344,63 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11335,50 +11344,63 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11335 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);11344 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
11336 defer case_vals.deinit(gpa);11345 defer case_vals.deinit(gpa);
1133711346
11347 var single_absorbed_item: Zir.Inst.Ref = .none;
11338 var absorbed_items: []const Zir.Inst.Ref = &.{};11348 var absorbed_items: []const Zir.Inst.Ref = &.{};
11339 var absorbed_ranges: []const Zir.Inst.Ref = &.{};11349 var absorbed_ranges: []const Zir.Inst.Ref = &.{};
1134011350
11341 const special_prong = extra.data.bits.special_prong;11351 const special_prongs = extra.data.bits.special_prongs;
11342 const special: SpecialProng = switch (special_prong) {11352 const has_else = special_prongs.hasElse();
11343 .none => .{11353 const has_under = special_prongs.hasUnder();
11344 .body = &.{},11354 const special_else: SpecialProng = if (has_else) blk: {
11345 .end = header_extra_index,11355 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
11346 .capture = .none,11356 const extra_body_start = header_extra_index + 1;
11347 .is_inline = false,11357 break :blk .{
11348 .has_tag_capture = false,11358 .body = sema.code.bodySlice(extra_body_start, info.body_len),
11349 },11359 .end = extra_body_start + info.body_len,
11350 .under, .@"else" => blk: {11360 .capture = info.capture,
11351 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);11361 .is_inline = info.is_inline,
11352 const extra_body_start = header_extra_index + 1;11362 .has_tag_capture = info.has_tag_capture,
11353 break :blk .{11363 };
11354 .body = sema.code.bodySlice(extra_body_start, info.body_len),11364 } else .{
11355 .end = extra_body_start + info.body_len,11365 .body = &.{},
11356 .capture = info.capture,11366 .end = header_extra_index,
11357 .is_inline = info.is_inline,11367 .capture = .none,
11358 .has_tag_capture = info.has_tag_capture,11368 .is_inline = false,
11359 };11369 .has_tag_capture = false,
11360 },11370 };
11361 .absorbing_under => blk: {11371 const special_under: SpecialProng = if (has_under) blk: {
11362 var extra_index = header_extra_index;11372 var extra_index = special_else.end;
11373 var trailing_items_len: usize = 0;
11374 if (special_prongs.hasOneAdditionalItem()) {
11375 single_absorbed_item = @enumFromInt(sema.code.extra[extra_index]);
11376 extra_index += 1;
11377 absorbed_items = @ptrCast(&single_absorbed_item);
11378 } else if (special_prongs.hasManyAdditionalItems()) {
11363 const items_len = sema.code.extra[extra_index];11379 const items_len = sema.code.extra[extra_index];
11364 extra_index += 1;11380 extra_index += 1;
11365 const ranges_len = sema.code.extra[extra_index];11381 const ranges_len = sema.code.extra[extra_index];
11366 extra_index += 1;11382 extra_index += 1;
11367 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);11383 absorbed_items = sema.code.refSlice(extra_index + 1, items_len);
11368 extra_index += 1;11384 absorbed_ranges = sema.code.refSlice(extra_index + 1 + items_len, ranges_len * 2);
11369 absorbed_items = sema.code.refSlice(extra_index, items_len);11385 trailing_items_len = items_len + ranges_len * 2;
11370 extra_index += items_len;11386 }
11371 absorbed_ranges = sema.code.refSlice(extra_index, ranges_len * 2);11387 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11372 extra_index += ranges_len * 2;11388 extra_index += 1 + trailing_items_len;
11373 break :blk .{11389 break :blk .{
11374 .body = sema.code.bodySlice(extra_index, info.body_len),11390 .body = sema.code.bodySlice(extra_index, info.body_len),
11375 .end = extra_index + info.body_len,11391 .end = extra_index + info.body_len,
11376 .capture = info.capture,11392 .capture = info.capture,
11377 .is_inline = info.is_inline,11393 .is_inline = info.is_inline,
11378 .has_tag_capture = info.has_tag_capture,11394 .has_tag_capture = info.has_tag_capture,
11379 };11395 };
11380 },11396 } else .{
11397 .body = &.{},
11398 .end = special_else.end,
11399 .capture = .none,
11400 .is_inline = false,
11401 .has_tag_capture = false,
11381 };11402 };
11403 const special_end = special_under.end;
1138211404
11383 // Duplicate checking variables later also used for `inline else`.11405 // Duplicate checking variables later also used for `inline else`.
11384 var seen_enum_fields: []?LazySrcLoc = &.{};11406 var seen_enum_fields: []?LazySrcLoc = &.{};
...@@ -11398,9 +11420,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11398,9 +11420,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11398 var else_error_ty: ?Type = null;11420 var else_error_ty: ?Type = null;
1139911421
11400 // Validate usage of '_' prongs.11422 // Validate usage of '_' prongs.
11401 if ((special_prong == .under or special_prong == .absorbing_under) and11423 if (has_under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) {
11402 !raw_operand_ty.isNonexhaustiveEnum(zcu))
11403 {
11404 const msg = msg: {11424 const msg = msg: {
11405 const msg = try sema.errMsg(11425 const msg = try sema.errMsg(
11406 src,11426 src,
...@@ -11409,7 +11429,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11409,7 +11429,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11409 );11429 );
11410 errdefer msg.destroy(gpa);11430 errdefer msg.destroy(gpa);
11411 try sema.errNote(11431 try sema.errNote(
11412 special_prong_src,11432 under_prong_src,
11413 msg,11433 msg,
11414 "'_' prong here",11434 "'_' prong here",
11415 .{},11435 .{},
...@@ -11443,14 +11463,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11443,14 +11463,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11443 cond_ty,11463 cond_ty,
11444 block.src(.{ .switch_case_item = .{11464 block.src(.{ .switch_case_item = .{
11445 .switch_node_offset = src_node_offset,11465 .switch_node_offset = src_node_offset,
11446 .case_idx = .special,11466 .case_idx = .special_under,
11447 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },11467 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
11448 } }),11468 } }),
11449 );11469 );
11450 }11470 }
11451 try sema.validateSwitchNoRange(block, @intCast(absorbed_ranges.len), cond_ty, src_node_offset);11471 try sema.validateSwitchNoRange(block, @intCast(absorbed_ranges.len), cond_ty, src_node_offset);
1145211472
11453 var extra_index: usize = special.end;11473 var extra_index: usize = special_end;
11454 {11474 {
11455 var scalar_i: u32 = 0;11475 var scalar_i: u32 = 0;
11456 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {11476 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
...@@ -11508,13 +11528,22 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11508,13 +11528,22 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11508 if (seen_src == null) break false;11528 if (seen_src == null) break false;
11509 } else true;11529 } else true;
1151011530
11511 if (special_prong == .@"else") {11531 if (has_else) {
11512 if (all_tags_handled and !cond_ty.isNonexhaustiveEnum(zcu)) return sema.fail(11532 if (all_tags_handled) {
11513 block,11533 if (cond_ty.isNonexhaustiveEnum(zcu)) {
11514 special_prong_src,11534 if (has_under) return sema.fail(
11515 "unreachable else prong; all cases already handled",11535 block,
11516 .{},11536 else_prong_src,
11517 );11537 "unreachable else prong; all explicit cases already handled",
11538 .{},
11539 );
11540 } else return sema.fail(
11541 block,
11542 else_prong_src,
11543 "unreachable else prong; all cases already handled",
11544 .{},
11545 );
11546 }
11518 } else if (!all_tags_handled) {11547 } else if (!all_tags_handled) {
11519 const msg = msg: {11548 const msg = msg: {
11520 const msg = try sema.errMsg(11549 const msg = try sema.errMsg(
...@@ -11532,7 +11561,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11532,7 +11561,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11532 i,11561 i,
11533 msg,11562 msg,
11534 "unhandled enumeration value: '{f}'",11563 "unhandled enumeration value: '{f}'",
11535 .{field_name.fmt(&zcu.intern_pool)},11564 .{field_name.fmt(ip)},
11536 );11565 );
11537 }11566 }
11538 try sema.errNote(11567 try sema.errNote(
...@@ -11544,11 +11573,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11544,11 +11573,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11544 break :msg msg;11573 break :msg msg;
11545 };11574 };
11546 return sema.failWithOwnedErrorMsg(block, msg);11575 return sema.failWithOwnedErrorMsg(block, msg);
11547 } else if (special_prong == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) {11576 } else if (special_prongs == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
11548 return sema.fail(11577 return sema.fail(
11549 block,11578 block,
11550 src,11579 src,
11551 "switch on non-exhaustive enum must include 'else' or '_' prong",11580 "switch on non-exhaustive enum must include 'else' or '_' prong or both",
11552 .{},11581 .{},
11553 );11582 );
11554 }11583 }
...@@ -11562,11 +11591,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11562,11 +11591,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11562 inst_data,11591 inst_data,
11563 scalar_cases_len,11592 scalar_cases_len,
11564 multi_cases_len,11593 multi_cases_len,
11565 .{ .body = special.body, .end = special.end, .src = special_prong_src },11594 .{ .body = special_else.body, .end = special_else.end, .src = else_prong_src },
11566 special_prong == .@"else",11595 has_else,
11567 ),11596 ),
11568 .int, .comptime_int => {11597 .int, .comptime_int => {
11569 var extra_index: usize = special.end;11598 var extra_index: usize = special_end;
11570 {11599 {
11571 var scalar_i: u32 = 0;11600 var scalar_i: u32 = 0;
11572 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {11601 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
...@@ -11648,10 +11677,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11648,10 +11677,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11648 const min_int = try cond_ty.minInt(pt, cond_ty);11677 const min_int = try cond_ty.minInt(pt, cond_ty);
11649 const max_int = try cond_ty.maxInt(pt, cond_ty);11678 const max_int = try cond_ty.maxInt(pt, cond_ty);
11650 if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {11679 if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {
11651 if (special_prong == .@"else") {11680 if (has_else) {
11652 return sema.fail(11681 return sema.fail(
11653 block,11682 block,
11654 special_prong_src,11683 else_prong_src,
11655 "unreachable else prong; all cases already handled",11684 "unreachable else prong; all cases already handled",
11656 .{},11685 .{},
11657 );11686 );
...@@ -11659,7 +11688,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11659,7 +11688,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11659 break :check_range;11688 break :check_range;
11660 }11689 }
11661 }11690 }
11662 if (special_prong != .@"else") {11691 if (special_prongs == .none) {
11663 return sema.fail(11692 return sema.fail(
11664 block,11693 block,
11665 src,11694 src,
...@@ -11670,7 +11699,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11670,7 +11699,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11670 }11699 }
11671 },11700 },
11672 .bool => {11701 .bool => {
11673 var extra_index: usize = special.end;11702 var extra_index: usize = special_end;
11674 {11703 {
11675 var scalar_i: u32 = 0;11704 var scalar_i: u32 = 0;
11676 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {11705 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
...@@ -11722,31 +11751,28 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11722,31 +11751,28 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11722 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);11751 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
11723 }11752 }
11724 }11753 }
11725 switch (special_prong) {11754 if (has_else) {
11726 .@"else" => {11755 if (true_count + false_count == 2) {
11727 if (true_count + false_count == 2) {11756 return sema.fail(
11728 return sema.fail(11757 block,
11729 block,11758 else_prong_src,
11730 special_prong_src,11759 "unreachable else prong; all cases already handled",
11731 "unreachable else prong; all cases already handled",11760 .{},
11732 .{},11761 );
11733 );11762 }
11734 }11763 } else {
11735 },11764 if (true_count + false_count < 2) {
11736 .under, .absorbing_under, .none => {11765 return sema.fail(
11737 if (true_count + false_count < 2) {11766 block,
11738 return sema.fail(11767 src,
11739 block,11768 "switch must handle all possibilities",
11740 src,11769 .{},
11741 "switch must handle all possibilities",11770 );
11742 .{},11771 }
11743 );
11744 }
11745 },
11746 }11772 }
11747 },11773 },
11748 .enum_literal, .void, .@"fn", .pointer, .type => {11774 .enum_literal, .void, .@"fn", .pointer, .type => {
11749 if (special_prong != .@"else") {11775 if (!has_else) {
11750 return sema.fail(11776 return sema.fail(
11751 block,11777 block,
11752 src,11778 src,
...@@ -11758,7 +11784,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11758,7 +11784,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11758 var seen_values = ValueSrcMap{};11784 var seen_values = ValueSrcMap{};
11759 defer seen_values.deinit(gpa);11785 defer seen_values.deinit(gpa);
1176011786
11761 var extra_index: usize = special.end;11787 var extra_index: usize = special_end;
11762 {11788 {
11763 var scalar_i: u32 = 0;11789 var scalar_i: u32 = 0;
11764 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {11790 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
...@@ -11831,6 +11857,16 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11831,6 +11857,16 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11831 }),11857 }),
11832 }11858 }
1183311859
11860 var special_members_only: ?SpecialProng = null;
11861 var special_members_only_src: LazySrcLoc = undefined;
11862 const special_generic, const special_generic_src = if (has_under) b: {
11863 if (has_else) {
11864 special_members_only = special_else;
11865 special_members_only_src = else_prong_src;
11866 }
11867 break :b .{ special_under, under_prong_src };
11868 } else .{ special_else, else_prong_src };
11869
11834 const spa: SwitchProngAnalysis = .{11870 const spa: SwitchProngAnalysis = .{
11835 .sema = sema,11871 .sema = sema,
11836 .parent_block = block,11872 .parent_block = block,
...@@ -11877,11 +11913,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11877,11 +11913,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11877 defer child_block.instructions.deinit(gpa);11913 defer child_block.instructions.deinit(gpa);
11878 defer merges.deinit(gpa);11914 defer merges.deinit(gpa);
1187911915
11880 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {11916 if (scalar_cases_len + multi_cases_len == 0 and
11917 special_members_only == null and
11918 !special_generic.is_inline)
11919 {
11881 if (empty_enum) {11920 if (empty_enum) {
11882 return .void_value;11921 return .void_value;
11883 }11922 }
11884 if (special_prong == .none) {11923 if (special_prongs == .none) {
11885 return sema.fail(block, src, "switch must handle all possibilities", .{});11924 return sema.fail(block, src, "switch must handle all possibilities", .{});
11886 }11925 }
11887 const init_cond = switch (operand) {11926 const init_cond = switch (operand) {
...@@ -11895,7 +11934,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11895,7 +11934,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11895 const ok = try block.addUnOp(.is_named_enum_value, init_cond);11934 const ok = try block.addUnOp(.is_named_enum_value, init_cond);
11896 try sema.addSafetyCheck(block, src, ok, .corrupt_switch);11935 try sema.addSafetyCheck(block, src, ok, .corrupt_switch);
11897 }11936 }
11898 if (err_set and try sema.maybeErrorUnwrap(block, special.body, init_cond, operand_src, false)) {11937 if (err_set and try sema.maybeErrorUnwrap(block, special_generic.body, init_cond, operand_src, false)) {
11899 return .unreachable_value;11938 return .unreachable_value;
11900 }11939 }
11901 }11940 }
...@@ -11915,7 +11954,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11915,7 +11954,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11915 cond_ty,11954 cond_ty,
11916 cond_val,11955 cond_val,
11917 src_node_offset,11956 src_node_offset,
11918 special,11957 special_members_only,
11958 special_generic,
11959 has_under,
11919 case_vals,11960 case_vals,
11920 scalar_cases_len,11961 scalar_cases_len,
11921 multi_cases_len,11962 multi_cases_len,
...@@ -11925,15 +11966,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11925,15 +11966,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11925 );11966 );
11926 }11967 }
1192711968
11928 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline and !extra.data.bits.has_continue) {11969 if (scalar_cases_len + multi_cases_len == 0 and
11970 special_members_only == null and
11971 !special_generic.is_inline and
11972 !extra.data.bits.has_continue)
11973 {
11929 return spa.resolveProngComptime(11974 return spa.resolveProngComptime(
11930 &child_block,11975 &child_block,
11931 .special,11976 .special,
11932 special.body,11977 special_generic.body,
11933 special.capture,11978 special_generic.capture,
11934 block.src(.{ .switch_capture = .{11979 block.src(.{ .switch_capture = .{
11935 .switch_node_offset = src_node_offset,11980 .switch_node_offset = src_node_offset,
11936 .case_idx = .special,11981 .case_idx = if (has_under) .special_under else .special_else,
11937 } }),11982 } }),
11938 undefined, // case_vals may be undefined for special prongs11983 undefined, // case_vals may be undefined for special prongs
11939 .none,11984 .none,
...@@ -11949,6 +11994,88 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11949,6 +11994,88 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11949 unreachable;11994 unreachable;
11950 }11995 }
1195111996
11997 var extra_case_vals: struct {
11998 items: std.ArrayListUnmanaged(Air.Inst.Ref),
11999 ranges: std.ArrayListUnmanaged([2]Air.Inst.Ref),
12000 } = .{ .items = .empty, .ranges = .empty };
12001 defer {
12002 extra_case_vals.items.deinit(gpa);
12003 extra_case_vals.ranges.deinit(gpa);
12004 }
12005
12006 // Runtime switch, if we have a special_members_only prong we need to unroll
12007 // it to a prong with explicit items.
12008 // Although this is potentially the same as `inline else` it does not count
12009 // towards the backward branch quota because it's an implementation detail.
12010 if (special_members_only) |special| gen: {
12011 assert(cond_ty.isNonexhaustiveEnum(zcu));
12012 _ = special;
12013
12014 var min_i: usize = math.maxInt(usize);
12015 var max_i: usize = 0;
12016 var seen_field_count: usize = 0;
12017 for (seen_enum_fields, 0..) |seen, enum_i| {
12018 if (seen != null) {
12019 seen_field_count += 1;
12020 } else {
12021 min_i = @min(min_i, enum_i);
12022 max_i = @max(max_i, enum_i);
12023 }
12024 }
12025 if (min_i == max_i) {
12026 seen_enum_fields[min_i] = special_members_only_src;
12027 const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
12028 const item_ref = Air.internedToRef(item_val.toIntern());
12029 try extra_case_vals.items.append(gpa, item_ref);
12030 break :gen;
12031 }
12032 const missing_field_count = seen_enum_fields.len - seen_field_count;
12033
12034 extra_case_vals.items = try .initCapacity(gpa, missing_field_count / 2);
12035 extra_case_vals.ranges = try .initCapacity(gpa, missing_field_count / 4);
12036 const int_ty = cond_ty.intTagType(zcu);
12037
12038 var last_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
12039 var first_ref = Air.internedToRef(last_val.toIntern());
12040 seen_enum_fields[min_i] = special_members_only_src;
12041 for (seen_enum_fields[(min_i + 1)..(max_i + 1)], (min_i + 1)..) |seen, enum_i| {
12042 if (seen != null) continue;
12043 seen_enum_fields[enum_i] = special_members_only_src;
12044
12045 const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(enum_i));
12046 const item_ref = Air.internedToRef(item_val.toIntern());
12047
12048 const is_next = is_next: {
12049 const prev_int = ip.indexToKey(last_val.toIntern()).enum_tag.int;
12050
12051 const result = try arith.incrementDefinedInt(sema, int_ty, .fromInterned(prev_int));
12052 if (result.overflow) break :is_next false;
12053
12054 const item_int = ip.indexToKey(item_val.toIntern()).enum_tag.int;
12055 break :is_next try sema.valuesEqual(.fromInterned(item_int), result.val, int_ty);
12056 };
12057
12058 if (is_next) {
12059 last_val = item_val;
12060 } else {
12061 const last_ref = Air.internedToRef(last_val.toIntern());
12062 if (first_ref == last_ref) {
12063 try extra_case_vals.items.append(gpa, first_ref);
12064 } else {
12065 try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
12066 }
12067 first_ref = item_ref;
12068 last_val = item_val;
12069 }
12070 }
12071 const last_ref = Air.internedToRef(last_val.toIntern());
12072 if (first_ref == last_ref) {
12073 try extra_case_vals.items.append(gpa, first_ref);
12074 } else {
12075 try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
12076 }
12077 }
12078
11952 const air_switch_ref = try sema.analyzeSwitchRuntimeBlock(12079 const air_switch_ref = try sema.analyzeSwitchRuntimeBlock(
11953 spa,12080 spa,
11954 &child_block,12081 &child_block,
...@@ -11960,14 +12087,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11960,14 +12087,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11960 cond_ty,12087 cond_ty,
11961 operand_src,12088 operand_src,
11962 case_vals,12089 case_vals,
11963 special,12090 special_generic,
11964 scalar_cases_len,12091 scalar_cases_len,
11965 multi_cases_len,12092 multi_cases_len,
11966 union_originally,12093 union_originally,
11967 raw_operand_ty,12094 raw_operand_ty,
11968 err_set,12095 err_set,
11969 src_node_offset,12096 src_node_offset,
11970 special_prong_src,12097 special_generic_src,
12098 has_under,
11971 seen_enum_fields,12099 seen_enum_fields,
11972 seen_errors,12100 seen_errors,
11973 range_set,12101 range_set,
...@@ -11975,6 +12103,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11975,6 +12103,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11975 false_count,12103 false_count,
11976 cond_dbg_node_index,12104 cond_dbg_node_index,
11977 false,12105 false,
12106 special_members_only,
12107 special_members_only_src,
12108 extra_case_vals.items.items,
12109 extra_case_vals.ranges.items,
11978 );12110 );
1197912111
11980 for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {12112 for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {
...@@ -12058,14 +12190,15 @@ fn analyzeSwitchRuntimeBlock(...@@ -12058,14 +12190,15 @@ fn analyzeSwitchRuntimeBlock(
12058 operand_ty: Type,12190 operand_ty: Type,
12059 operand_src: LazySrcLoc,12191 operand_src: LazySrcLoc,
12060 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),12192 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
12061 special: SpecialProng,12193 else_prong: SpecialProng,
12062 scalar_cases_len: usize,12194 scalar_cases_len: usize,
12063 multi_cases_len: usize,12195 multi_cases_len: usize,
12064 union_originally: bool,12196 union_originally: bool,
12065 maybe_union_ty: Type,12197 maybe_union_ty: Type,
12066 err_set: bool,12198 err_set: bool,
12067 switch_node_offset: std.zig.Ast.Node.Offset,12199 switch_node_offset: std.zig.Ast.Node.Offset,
12068 special_prong_src: LazySrcLoc,12200 else_prong_src: LazySrcLoc,
12201 else_prong_is_underscore: bool,
12069 seen_enum_fields: []?LazySrcLoc,12202 seen_enum_fields: []?LazySrcLoc,
12070 seen_errors: SwitchErrorSet,12203 seen_errors: SwitchErrorSet,
12071 range_set: RangeSet,12204 range_set: RangeSet,
...@@ -12073,6 +12206,11 @@ fn analyzeSwitchRuntimeBlock(...@@ -12073,6 +12206,11 @@ fn analyzeSwitchRuntimeBlock(
12073 false_count: u8,12206 false_count: u8,
12074 cond_dbg_node_index: Zir.Inst.Index,12207 cond_dbg_node_index: Zir.Inst.Index,
12075 allow_err_code_unwrap: bool,12208 allow_err_code_unwrap: bool,
12209 extra_prong: ?SpecialProng,
12210 /// May be `undefined` if `extra_prong` is `null`
12211 extra_prong_src: LazySrcLoc,
12212 extra_prong_items: []const Air.Inst.Ref,
12213 extra_prong_ranges: []const [2]Air.Inst.Ref,
12076) CompileError!Air.Inst.Ref {12214) CompileError!Air.Inst.Ref {
12077 const pt = sema.pt;12215 const pt = sema.pt;
12078 const zcu = pt.zcu;12216 const zcu = pt.zcu;
...@@ -12096,7 +12234,7 @@ fn analyzeSwitchRuntimeBlock(...@@ -12096,7 +12234,7 @@ fn analyzeSwitchRuntimeBlock(
12096 case_block.need_debug_scope = null; // this body is emitted regardless12234 case_block.need_debug_scope = null; // this body is emitted regardless
12097 defer case_block.instructions.deinit(gpa);12235 defer case_block.instructions.deinit(gpa);
1209812236
12099 var extra_index: usize = special.end;12237 var extra_index: usize = else_prong.end;
1210012238
12101 var scalar_i: usize = 0;12239 var scalar_i: usize = 0;
12102 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {12240 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
...@@ -12158,23 +12296,42 @@ fn analyzeSwitchRuntimeBlock(...@@ -12158,23 +12296,42 @@ fn analyzeSwitchRuntimeBlock(
1215812296
12159 var cases_len = scalar_cases_len;12297 var cases_len = scalar_cases_len;
12160 var case_val_idx: usize = scalar_cases_len;12298 var case_val_idx: usize = scalar_cases_len;
12299 const multi_cases_len_with_extra_prong = multi_cases_len + @intFromBool(extra_prong != null);
12161 var multi_i: u32 = 0;12300 var multi_i: u32 = 0;
12162 while (multi_i < multi_cases_len) : (multi_i += 1) {12301 while (multi_i < multi_cases_len_with_extra_prong) : (multi_i += 1) {
12163 const items_len = sema.code.extra[extra_index];12302 const is_extra_prong = multi_i == multi_cases_len;
12164 extra_index += 1;12303 var items: []const Air.Inst.Ref = undefined;
12165 const ranges_len = sema.code.extra[extra_index];12304 var info: Zir.Inst.SwitchBlock.ProngInfo = undefined;
12166 extra_index += 1;12305 var ranges: []const [2]Air.Inst.Ref = undefined;
12167 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);12306 var body: []const Zir.Inst.Index = undefined;
12168 extra_index += 1 + items_len + 2 * ranges_len;12307 if (is_extra_prong) {
12308 const prong = extra_prong.?;
12309 items = extra_prong_items;
12310 ranges = extra_prong_ranges;
12311 body = prong.body;
12312 info = .{
12313 .body_len = undefined,
12314 .capture = prong.capture,
12315 .is_inline = prong.is_inline,
12316 .has_tag_capture = prong.has_tag_capture,
12317 };
12318 } else {
12319 @branchHint(.likely);
12320 const items_len = sema.code.extra[extra_index];
12321 extra_index += 1;
12322 const ranges_len = sema.code.extra[extra_index];
12323 extra_index += 1;
12324 info = @bitCast(sema.code.extra[extra_index]);
12325 extra_index += 1 + items_len + ranges_len * 2;
1216912326
12170 const items = case_vals.items[case_val_idx..][0..items_len];12327 items = case_vals.items[case_val_idx..][0..items_len];
12171 case_val_idx += items_len;12328 case_val_idx += items_len;
12172 // TODO: @ptrCast slice once Sema supports it12329 ranges = @ptrCast(case_vals.items[case_val_idx..][0 .. ranges_len * 2]);
12173 const ranges: []const [2]Air.Inst.Ref = @as([*]const [2]Air.Inst.Ref, @ptrCast(case_vals.items[case_val_idx..]))[0..ranges_len];12330 case_val_idx += ranges_len * 2;
12174 case_val_idx += ranges_len * 2;
1217512331
12176 const body = sema.code.bodySlice(extra_index, info.body_len);12332 body = sema.code.bodySlice(extra_index, info.body_len);
12177 extra_index += info.body_len;12333 extra_index += info.body_len;
12334 }
1217812335
12179 case_block.instructions.shrinkRetainingCapacity(0);12336 case_block.instructions.shrinkRetainingCapacity(0);
12180 case_block.error_return_trace_index = child_block.error_return_trace_index;12337 case_block.error_return_trace_index = child_block.error_return_trace_index;
...@@ -12184,14 +12341,29 @@ fn analyzeSwitchRuntimeBlock(...@@ -12184,14 +12341,29 @@ fn analyzeSwitchRuntimeBlock(
12184 var emit_bb = false;12341 var emit_bb = false;
1218512342
12186 for (ranges, 0..) |range_items, range_i| {12343 for (ranges, 0..) |range_items, range_i| {
12187 var item = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable;12344 var item = sema.resolveConstDefinedValue(block, .unneeded, range_items[0], undefined) catch unreachable;
12188 const item_last = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable;12345 const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_items[1], undefined) catch unreachable;
1218912346
12190 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({12347 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
12191 // Previous validation has resolved any possible lazy values.12348 // Previous validation has resolved any possible lazy values.
12192 const result = try arith.incrementDefinedInt(sema, operand_ty, item);12349 const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
12350 .int => .{ item, operand_ty },
12351 .@"enum" => b: {
12352 const int_val = Value.fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int);
12353 break :b .{ int_val, int_val.typeOf(zcu) };
12354 },
12355 else => unreachable,
12356 };
12357 const result = try arith.incrementDefinedInt(sema, int_ty, int_val);
12193 assert(!result.overflow);12358 assert(!result.overflow);
12194 item = result.val;12359 item = switch (operand_ty.zigTypeTag(zcu)) {
12360 .int => result.val,
12361 .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{
12362 .ty = operand_ty.toIntern(),
12363 .int = result.val.toIntern(),
12364 } })),
12365 else => unreachable,
12366 };
12195 }) {12367 }) {
12196 cases_len += 1;12368 cases_len += 1;
1219712369
...@@ -12200,11 +12372,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -12200,11 +12372,14 @@ fn analyzeSwitchRuntimeBlock(
12200 case_block.instructions.shrinkRetainingCapacity(0);12372 case_block.instructions.shrinkRetainingCapacity(0);
12201 case_block.error_return_trace_index = child_block.error_return_trace_index;12373 case_block.error_return_trace_index = child_block.error_return_trace_index;
1220212374
12203 if (emit_bb) try sema.emitBackwardBranch(block, block.src(.{ .switch_case_item = .{12375 if (emit_bb) {
12204 .switch_node_offset = switch_node_offset,12376 const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
12205 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },12377 .switch_node_offset = switch_node_offset,
12206 .item_idx = .{ .kind = .range, .index = @intCast(range_i) },12378 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12207 } }));12379 .item_idx = .{ .kind = .range, .index = @intCast(range_i) },
12380 } });
12381 try sema.emitBackwardBranch(block, bb_src);
12382 }
12208 emit_bb = true;12383 emit_bb = true;
1220912384
12210 const prong_hint = try spa.analyzeProngRuntime(12385 const prong_hint = try spa.analyzeProngRuntime(
...@@ -12249,11 +12424,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -12249,11 +12424,14 @@ fn analyzeSwitchRuntimeBlock(
12249 break :blk field_ty.zigTypeTag(zcu) != .noreturn;12424 break :blk field_ty.zigTypeTag(zcu) != .noreturn;
12250 } else true;12425 } else true;
1225112426
12252 if (emit_bb) try sema.emitBackwardBranch(block, block.src(.{ .switch_case_item = .{12427 if (emit_bb) {
12253 .switch_node_offset = switch_node_offset,12428 const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
12254 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },12429 .switch_node_offset = switch_node_offset,
12255 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },12430 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12256 } }));12431 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
12432 } });
12433 try sema.emitBackwardBranch(block, bb_src);
12434 }
12257 emit_bb = true;12435 emit_bb = true;
1225812436
12259 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {12437 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
...@@ -12329,11 +12507,11 @@ fn analyzeSwitchRuntimeBlock(...@@ -12329,11 +12507,11 @@ fn analyzeSwitchRuntimeBlock(
12329 try branch_hints.append(gpa, prong_hint);12507 try branch_hints.append(gpa, prong_hint);
1233012508
12331 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +12509 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12332 items.len + 2 * ranges_len +12510 items.len + ranges.len * 2 +
12333 case_block.instructions.items.len);12511 case_block.instructions.items.len);
12334 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{12512 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12335 .items_len = @intCast(items.len),12513 .items_len = @intCast(items.len),
12336 .ranges_len = @intCast(ranges_len),12514 .ranges_len = @intCast(ranges.len),
12337 .body_len = @intCast(case_block.instructions.items.len),12515 .body_len = @intCast(case_block.instructions.items.len),
12338 }));12516 }));
1233912517
...@@ -12350,12 +12528,14 @@ fn analyzeSwitchRuntimeBlock(...@@ -12350,12 +12528,14 @@ fn analyzeSwitchRuntimeBlock(
12350 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));12528 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12351 }12529 }
1235212530
12353 const else_body: []const Air.Inst.Index = if (special.body.len != 0 or case_block.wantSafety()) else_body: {12531 const else_body: []const Air.Inst.Index = if (else_prong.body.len != 0 or case_block.wantSafety()) else_body: {
12354 var emit_bb = false;12532 var emit_bb = false;
12355 if (special.is_inline) switch (operand_ty.zigTypeTag(zcu)) {12533 // If this is true we must have a 'true' else prong and not an underscore because
12534 // underscore prongs can never be inlined. We've already checked for this.
12535 if (else_prong.is_inline) switch (operand_ty.zigTypeTag(zcu)) {
12356 .@"enum" => {12536 .@"enum" => {
12357 if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) {12537 if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
12358 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{12538 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
12359 operand_ty.fmt(pt),12539 operand_ty.fmt(pt),
12360 });12540 });
12361 }12541 }
...@@ -12374,22 +12554,22 @@ fn analyzeSwitchRuntimeBlock(...@@ -12374,22 +12554,22 @@ fn analyzeSwitchRuntimeBlock(
12374 break :blk field_ty.zigTypeTag(zcu) != .noreturn;12554 break :blk field_ty.zigTypeTag(zcu) != .noreturn;
12375 } else true;12555 } else true;
1237612556
12377 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);12557 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12378 emit_bb = true;12558 emit_bb = true;
1237912559
12380 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {12560 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
12381 break :h try spa.analyzeProngRuntime(12561 break :h try spa.analyzeProngRuntime(
12382 &case_block,12562 &case_block,
12383 .special,12563 .special,
12384 special.body,12564 else_prong.body,
12385 special.capture,12565 else_prong.capture,
12386 child_block.src(.{ .switch_capture = .{12566 child_block.src(.{ .switch_capture = .{
12387 .switch_node_offset = switch_node_offset,12567 .switch_node_offset = switch_node_offset,
12388 .case_idx = .special,12568 .case_idx = .special_else,
12389 } }),12569 } }),
12390 &.{item_ref},12570 &.{item_ref},
12391 item_ref,12571 item_ref,
12392 special.has_tag_capture,12572 else_prong.has_tag_capture,
12393 );12573 );
12394 } else h: {12574 } else h: {
12395 _ = try case_block.addNoOp(.unreach);12575 _ = try case_block.addNoOp(.unreach);
...@@ -12411,7 +12591,7 @@ fn analyzeSwitchRuntimeBlock(...@@ -12411,7 +12591,7 @@ fn analyzeSwitchRuntimeBlock(
12411 },12591 },
12412 .error_set => {12592 .error_set => {
12413 if (operand_ty.isAnyError(zcu)) {12593 if (operand_ty.isAnyError(zcu)) {
12414 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{12594 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
12415 operand_ty.fmt(pt),12595 operand_ty.fmt(pt),
12416 });12596 });
12417 }12597 }
...@@ -12430,21 +12610,21 @@ fn analyzeSwitchRuntimeBlock(...@@ -12430,21 +12610,21 @@ fn analyzeSwitchRuntimeBlock(
12430 case_block.instructions.shrinkRetainingCapacity(0);12610 case_block.instructions.shrinkRetainingCapacity(0);
12431 case_block.error_return_trace_index = child_block.error_return_trace_index;12611 case_block.error_return_trace_index = child_block.error_return_trace_index;
1243212612
12433 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);12613 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12434 emit_bb = true;12614 emit_bb = true;
1243512615
12436 const prong_hint = try spa.analyzeProngRuntime(12616 const prong_hint = try spa.analyzeProngRuntime(
12437 &case_block,12617 &case_block,
12438 .special,12618 .special,
12439 special.body,12619 else_prong.body,
12440 special.capture,12620 else_prong.capture,
12441 child_block.src(.{ .switch_capture = .{12621 child_block.src(.{ .switch_capture = .{
12442 .switch_node_offset = switch_node_offset,12622 .switch_node_offset = switch_node_offset,
12443 .case_idx = .special,12623 .case_idx = .special_else,
12444 } }),12624 } }),
12445 &.{item_ref},12625 &.{item_ref},
12446 item_ref,12626 item_ref,
12447 special.has_tag_capture,12627 else_prong.has_tag_capture,
12448 );12628 );
12449 try branch_hints.append(gpa, prong_hint);12629 try branch_hints.append(gpa, prong_hint);
1245012630
...@@ -12470,21 +12650,21 @@ fn analyzeSwitchRuntimeBlock(...@@ -12470,21 +12650,21 @@ fn analyzeSwitchRuntimeBlock(
12470 case_block.instructions.shrinkRetainingCapacity(0);12650 case_block.instructions.shrinkRetainingCapacity(0);
12471 case_block.error_return_trace_index = child_block.error_return_trace_index;12651 case_block.error_return_trace_index = child_block.error_return_trace_index;
1247212652
12473 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);12653 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12474 emit_bb = true;12654 emit_bb = true;
1247512655
12476 const prong_hint = try spa.analyzeProngRuntime(12656 const prong_hint = try spa.analyzeProngRuntime(
12477 &case_block,12657 &case_block,
12478 .special,12658 .special,
12479 special.body,12659 else_prong.body,
12480 special.capture,12660 else_prong.capture,
12481 child_block.src(.{ .switch_capture = .{12661 child_block.src(.{ .switch_capture = .{
12482 .switch_node_offset = switch_node_offset,12662 .switch_node_offset = switch_node_offset,
12483 .case_idx = .special,12663 .case_idx = .special_else,
12484 } }),12664 } }),
12485 &.{item_ref},12665 &.{item_ref},
12486 item_ref,12666 item_ref,
12487 special.has_tag_capture,12667 else_prong.has_tag_capture,
12488 );12668 );
12489 try branch_hints.append(gpa, prong_hint);12669 try branch_hints.append(gpa, prong_hint);
1249012670
...@@ -12507,21 +12687,21 @@ fn analyzeSwitchRuntimeBlock(...@@ -12507,21 +12687,21 @@ fn analyzeSwitchRuntimeBlock(
12507 case_block.instructions.shrinkRetainingCapacity(0);12687 case_block.instructions.shrinkRetainingCapacity(0);
12508 case_block.error_return_trace_index = child_block.error_return_trace_index;12688 case_block.error_return_trace_index = child_block.error_return_trace_index;
1250912689
12510 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);12690 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12511 emit_bb = true;12691 emit_bb = true;
1251212692
12513 const prong_hint = try spa.analyzeProngRuntime(12693 const prong_hint = try spa.analyzeProngRuntime(
12514 &case_block,12694 &case_block,
12515 .special,12695 .special,
12516 special.body,12696 else_prong.body,
12517 special.capture,12697 else_prong.capture,
12518 child_block.src(.{ .switch_capture = .{12698 child_block.src(.{ .switch_capture = .{
12519 .switch_node_offset = switch_node_offset,12699 .switch_node_offset = switch_node_offset,
12520 .case_idx = .special,12700 .case_idx = .special_else,
12521 } }),12701 } }),
12522 &.{.bool_true},12702 &.{.bool_true},
12523 .bool_true,12703 .bool_true,
12524 special.has_tag_capture,12704 else_prong.has_tag_capture,
12525 );12705 );
12526 try branch_hints.append(gpa, prong_hint);12706 try branch_hints.append(gpa, prong_hint);
1252712707
...@@ -12542,21 +12722,21 @@ fn analyzeSwitchRuntimeBlock(...@@ -12542,21 +12722,21 @@ fn analyzeSwitchRuntimeBlock(
12542 case_block.instructions.shrinkRetainingCapacity(0);12722 case_block.instructions.shrinkRetainingCapacity(0);
12543 case_block.error_return_trace_index = child_block.error_return_trace_index;12723 case_block.error_return_trace_index = child_block.error_return_trace_index;
1254412724
12545 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);12725 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12546 emit_bb = true;12726 emit_bb = true;
1254712727
12548 const prong_hint = try spa.analyzeProngRuntime(12728 const prong_hint = try spa.analyzeProngRuntime(
12549 &case_block,12729 &case_block,
12550 .special,12730 .special,
12551 special.body,12731 else_prong.body,
12552 special.capture,12732 else_prong.capture,
12553 child_block.src(.{ .switch_capture = .{12733 child_block.src(.{ .switch_capture = .{
12554 .switch_node_offset = switch_node_offset,12734 .switch_node_offset = switch_node_offset,
12555 .case_idx = .special,12735 .case_idx = .special_else,
12556 } }),12736 } }),
12557 &.{.bool_false},12737 &.{.bool_false},
12558 .bool_false,12738 .bool_false,
12559 special.has_tag_capture,12739 else_prong.has_tag_capture,
12560 );12740 );
12561 try branch_hints.append(gpa, prong_hint);12741 try branch_hints.append(gpa, prong_hint);
1256212742
...@@ -12572,7 +12752,7 @@ fn analyzeSwitchRuntimeBlock(...@@ -12572,7 +12752,7 @@ fn analyzeSwitchRuntimeBlock(
12572 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));12752 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12573 }12753 }
12574 },12754 },
12575 else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{12755 else => return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
12576 operand_ty.fmt(pt),12756 operand_ty.fmt(pt),
12577 }),12757 }),
12578 };12758 };
...@@ -12581,7 +12761,7 @@ fn analyzeSwitchRuntimeBlock(...@@ -12581,7 +12761,7 @@ fn analyzeSwitchRuntimeBlock(
12581 case_block.error_return_trace_index = child_block.error_return_trace_index;12761 case_block.error_return_trace_index = child_block.error_return_trace_index;
1258212762
12583 if (zcu.backendSupportsFeature(.is_named_enum_value) and12763 if (zcu.backendSupportsFeature(.is_named_enum_value) and
12584 special.body.len != 0 and block.wantSafety() and12764 else_prong.body.len != 0 and block.wantSafety() and
12585 operand_ty.zigTypeTag(zcu) == .@"enum" and12765 operand_ty.zigTypeTag(zcu) == .@"enum" and
12586 (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally))12766 (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally))
12587 {12767 {
...@@ -12590,7 +12770,12 @@ fn analyzeSwitchRuntimeBlock(...@@ -12590,7 +12770,12 @@ fn analyzeSwitchRuntimeBlock(
12590 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);12770 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
12591 }12771 }
1259212772
12593 const analyze_body = if (union_originally and !special.is_inline)12773 const else_src_idx: LazySrcLoc.Offset.SwitchCaseIndex = if (else_prong_is_underscore)
12774 .special_under
12775 else
12776 .special_else;
12777
12778 const analyze_body = if (union_originally and !else_prong.is_inline)
12594 for (seen_enum_fields, 0..) |seen_field, index| {12779 for (seen_enum_fields, 0..) |seen_field, index| {
12595 if (seen_field != null) continue;12780 if (seen_field != null) continue;
12596 const union_obj = zcu.typeToUnion(maybe_union_ty).?;12781 const union_obj = zcu.typeToUnion(maybe_union_ty).?;
...@@ -12599,20 +12784,20 @@ fn analyzeSwitchRuntimeBlock(...@@ -12599,20 +12784,20 @@ fn analyzeSwitchRuntimeBlock(
12599 } else false12784 } else false
12600 else12785 else
12601 true;12786 true;
12602 const else_hint: std.builtin.BranchHint = if (special.body.len != 0 and err_set and12787 const else_hint: std.builtin.BranchHint = if (else_prong.body.len != 0 and err_set and
12603 try sema.maybeErrorUnwrap(&case_block, special.body, operand, operand_src, allow_err_code_unwrap))12788 try sema.maybeErrorUnwrap(&case_block, else_prong.body, operand, operand_src, allow_err_code_unwrap))
12604 h: {12789 h: {
12605 // nothing to do here. weight against error branch12790 // nothing to do here. weight against error branch
12606 break :h .unlikely;12791 break :h .unlikely;
12607 } else if (special.body.len != 0 and analyze_body and !special.is_inline) h: {12792 } else if (else_prong.body.len != 0 and analyze_body and !else_prong.is_inline) h: {
12608 break :h try spa.analyzeProngRuntime(12793 break :h try spa.analyzeProngRuntime(
12609 &case_block,12794 &case_block,
12610 .special,12795 .special,
12611 special.body,12796 else_prong.body,
12612 special.capture,12797 else_prong.capture,
12613 child_block.src(.{ .switch_capture = .{12798 child_block.src(.{ .switch_capture = .{
12614 .switch_node_offset = switch_node_offset,12799 .switch_node_offset = switch_node_offset,
12615 .case_idx = .special,12800 .case_idx = else_src_idx,
12616 } }),12801 } }),
12617 undefined, // case_vals may be undefined for special prongs12802 undefined, // case_vals may be undefined for special prongs
12618 .none,12803 .none,
...@@ -12686,7 +12871,9 @@ fn resolveSwitchComptimeLoop(...@@ -12686,7 +12871,9 @@ fn resolveSwitchComptimeLoop(
12686 cond_ty: Type,12871 cond_ty: Type,
12687 init_cond_val: Value,12872 init_cond_val: Value,
12688 switch_node_offset: std.zig.Ast.Node.Offset,12873 switch_node_offset: std.zig.Ast.Node.Offset,
12689 special: SpecialProng,12874 special_members_only: ?SpecialProng,
12875 special_generic: SpecialProng,
12876 special_generic_is_under: bool,
12690 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),12877 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
12691 scalar_cases_len: u32,12878 scalar_cases_len: u32,
12692 multi_cases_len: u32,12879 multi_cases_len: u32,
...@@ -12706,7 +12893,9 @@ fn resolveSwitchComptimeLoop(...@@ -12706,7 +12893,9 @@ fn resolveSwitchComptimeLoop(
12706 cond_val,12893 cond_val,
12707 cond_ty,12894 cond_ty,
12708 switch_node_offset,12895 switch_node_offset,
12709 special,12896 special_members_only,
12897 special_generic,
12898 special_generic_is_under,
12710 case_vals,12899 case_vals,
12711 scalar_cases_len,12900 scalar_cases_len,
12712 multi_cases_len,12901 multi_cases_len,
...@@ -12754,17 +12943,20 @@ fn resolveSwitchComptime(...@@ -12754,17 +12943,20 @@ fn resolveSwitchComptime(
12754 operand_val: Value,12943 operand_val: Value,
12755 operand_ty: Type,12944 operand_ty: Type,
12756 switch_node_offset: std.zig.Ast.Node.Offset,12945 switch_node_offset: std.zig.Ast.Node.Offset,
12757 special: SpecialProng,12946 special_members_only: ?SpecialProng,
12947 special_generic: SpecialProng,
12948 special_generic_is_under: bool,
12758 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),12949 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
12759 scalar_cases_len: u32,12950 scalar_cases_len: u32,
12760 multi_cases_len: u32,12951 multi_cases_len: u32,
12761 err_set: bool,12952 err_set: bool,
12762 empty_enum: bool,12953 empty_enum: bool,
12763) CompileError!Air.Inst.Ref {12954) CompileError!Air.Inst.Ref {
12955 const zcu = sema.pt.zcu;
12764 const merges = &child_block.label.?.merges;12956 const merges = &child_block.label.?.merges;
12765 const resolved_operand_val = try sema.resolveLazyValue(operand_val);12957 const resolved_operand_val = try sema.resolveLazyValue(operand_val);
1276612958
12767 var extra_index: usize = special.end;12959 var extra_index: usize = special_generic.end;
12768 {12960 {
12769 var scalar_i: usize = 0;12961 var scalar_i: usize = 0;
12770 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {12962 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
...@@ -12865,23 +13057,45 @@ fn resolveSwitchComptime(...@@ -12865,23 +13057,45 @@ fn resolveSwitchComptime(
12865 extra_index += info.body_len;13057 extra_index += info.body_len;
12866 }13058 }
12867 }13059 }
12868 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special.body, cond_operand);13060 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special_generic.body, cond_operand);
12869 if (empty_enum) {13061 if (empty_enum) {
12870 return .void_value;13062 return .void_value;
12871 }13063 }
13064 if (special_members_only) |special| {
13065 assert(operand_ty.isNonexhaustiveEnum(zcu));
13066 if (operand_ty.enumTagFieldIndex(operand_val, zcu)) |_| {
13067 return spa.resolveProngComptime(
13068 child_block,
13069 .special,
13070 special.body,
13071 special.capture,
13072 child_block.src(.{ .switch_capture = .{
13073 .switch_node_offset = switch_node_offset,
13074 .case_idx = .special_else,
13075 } }),
13076 undefined, // case_vals may be undefined for special prongs
13077 if (special.is_inline) cond_operand else .none,
13078 special.has_tag_capture,
13079 merges,
13080 );
13081 }
13082 }
1287213083
12873 return spa.resolveProngComptime(13084 return spa.resolveProngComptime(
12874 child_block,13085 child_block,
12875 .special,13086 .special,
12876 special.body,13087 special_generic.body,
12877 special.capture,13088 special_generic.capture,
12878 child_block.src(.{ .switch_capture = .{13089 child_block.src(.{ .switch_capture = .{
12879 .switch_node_offset = switch_node_offset,13090 .switch_node_offset = switch_node_offset,
12880 .case_idx = .special,13091 .case_idx = if (special_generic_is_under)
13092 .special_under
13093 else
13094 .special_else,
12881 } }),13095 } }),
12882 undefined, // case_vals may be undefined for special prongs13096 undefined, // case_vals may be undefined for special prongs
12883 if (special.is_inline) cond_operand else .none,13097 if (special_generic.is_inline) cond_operand else .none,
12884 special.has_tag_capture,13098 special_generic.has_tag_capture,
12885 merges,13099 merges,
12886 );13100 );
12887}13101}
src/Zcu.zig+56-32
...@@ -1677,34 +1677,47 @@ pub const SrcLoc = struct {...@@ -1677,34 +1677,47 @@ pub const SrcLoc = struct {
1677 return tree.nodeToSpan(condition);1677 return tree.nodeToSpan(condition);
1678 },1678 },
16791679
1680 .node_offset_switch_special_prong => |node_off| {1680 .node_offset_switch_else_prong => |node_off| {
1681 const tree = try src_loc.file_scope.getTree(zcu);1681 const tree = try src_loc.file_scope.getTree(zcu);
1682 const switch_node = node_off.toAbsolute(src_loc.base_node);1682 const switch_node = node_off.toAbsolute(src_loc.base_node);
1683 _, const extra_index = tree.nodeData(switch_node).node_and_extra;1683 _, const extra_index = tree.nodeData(switch_node).node_and_extra;
1684 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);1684 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
1685 for (case_nodes) |case_node| {1685 for (case_nodes) |case_node| {
1686 const case = tree.fullSwitchCase(case_node).?;1686 const case = tree.fullSwitchCase(case_node).?;
1687 if (case.isSpecial(tree)) |special_node| {1687 if (case.ast.values.len == 0) {
1688 return tree.tokensToSpan(1688 return tree.nodeToSpan(case_node);
1689 tree.firstToken(case_node),
1690 tree.lastToken(case_node),
1691 tree.nodeMainToken(special_node.unwrap() orelse case_node),
1692 );
1693 }1689 }
1694 } else unreachable;1690 } else unreachable;
1695 },1691 },
16961692
1697 .node_offset_switch_range => |node_off| {1693 .node_offset_switch_under_prong => |node_off| {
1698 const tree = try src_loc.file_scope.getTree(zcu);1694 const tree = try src_loc.file_scope.getTree(zcu);
1699 const switch_node = node_off.toAbsolute(src_loc.base_node);1695 const switch_node = node_off.toAbsolute(src_loc.base_node);
1700 _, const extra_index = tree.nodeData(switch_node).node_and_extra;1696 _, const extra_index = tree.nodeData(switch_node).node_and_extra;
1701 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);1697 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
1702 for (case_nodes) |case_node| {1698 for (case_nodes) |case_node| {
1703 const case = tree.fullSwitchCase(case_node).?;1699 const case = tree.fullSwitchCase(case_node).?;
1704 if (case.isSpecial(tree)) |maybe_else| {1700 for (case.ast.values) |val| {
1705 if (maybe_else == .none) continue;1701 if (tree.nodeTag(val) == .identifier and
1702 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_"))
1703 {
1704 return tree.tokensToSpan(
1705 tree.firstToken(case_node),
1706 tree.lastToken(case_node),
1707 tree.nodeMainToken(val),
1708 );
1709 }
1706 }1710 }
1711 } else unreachable;
1712 },
17071713
1714 .node_offset_switch_range => |node_off| {
1715 const tree = try src_loc.file_scope.getTree(zcu);
1716 const switch_node = node_off.toAbsolute(src_loc.base_node);
1717 _, const extra_index = tree.nodeData(switch_node).node_and_extra;
1718 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
1719 for (case_nodes) |case_node| {
1720 const case = tree.fullSwitchCase(case_node).?;
1708 for (case.ast.values) |item_node| {1721 for (case.ast.values) |item_node| {
1709 if (tree.nodeTag(item_node) == .switch_range) {1722 if (tree.nodeTag(item_node) == .switch_range) {
1710 return tree.nodeToSpan(item_node);1723 return tree.nodeToSpan(item_node);
...@@ -2109,32 +2122,35 @@ pub const SrcLoc = struct {...@@ -2109,32 +2122,35 @@ pub const SrcLoc = struct {
21092122
2110 var multi_i: u32 = 0;2123 var multi_i: u32 = 0;
2111 var scalar_i: u32 = 0;2124 var scalar_i: u32 = 0;
2112 var found_special = false;
2113 var underscore_node: Ast.Node.OptionalIndex = .none;2125 var underscore_node: Ast.Node.OptionalIndex = .none;
2114 const case = for (case_nodes) |case_node| {2126 const case = case: for (case_nodes) |case_node| {
2115 const case = tree.fullSwitchCase(case_node).?;2127 const case = tree.fullSwitchCase(case_node).?;
2116 const is_special = special: {2128 if (case.ast.values.len == 0) {
2117 if (found_special) break :special false;2129 if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special_else) {
2118 if (case.isSpecial(tree)) |special_node| {2130 break :case case;
2119 underscore_node = special_node;
2120 found_special = true;
2121 break :special true;
2122 }2131 }
2123 break :special false;2132 continue :case;
2124 };
2125 if (is_special) {
2126 if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special) {
2127 break case;
2128 }
2129 continue;
2130 }2133 }
2134 if (underscore_node == .none) for (case.ast.values) |val_node| {
2135 if (tree.nodeTag(val_node) == .identifier and
2136 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val_node)), "_"))
2137 {
2138 underscore_node = val_node.toOptional();
2139 if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special_under) {
2140 break :case case;
2141 }
2142 continue :case;
2143 }
2144 };
21312145
2132 const is_multi = case.ast.values.len != 1 or2146 const is_multi = case.ast.values.len != 1 or
2133 tree.nodeTag(case.ast.values[0]) == .switch_range;2147 tree.nodeTag(case.ast.values[0]) == .switch_range;
21342148
2135 switch (want_case_idx.kind) {2149 switch (want_case_idx.kind) {
2136 .scalar => if (!is_multi and want_case_idx.index == scalar_i) break case,2150 .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,2151 break :case case,
2152 .multi => if (is_multi and want_case_idx.index == multi_i)
2153 break :case case,
2138 }2154 }
21392155
2140 if (is_multi) {2156 if (is_multi) {
...@@ -2148,7 +2164,10 @@ pub const SrcLoc = struct {...@@ -2148,7 +2164,10 @@ pub const SrcLoc = struct {
2148 .switch_case_item,2164 .switch_case_item,
2149 .switch_case_item_range_first,2165 .switch_case_item_range_first,
2150 .switch_case_item_range_last,2166 .switch_case_item_range_last,
2151 => |x| x.item_idx,2167 => |x| item_idx: {
2168 assert(want_case_idx != LazySrcLoc.Offset.SwitchCaseIndex.special_else);
2169 break :item_idx x.item_idx;
2170 },
2152 .switch_capture, .switch_tag_capture => {2171 .switch_capture, .switch_tag_capture => {
2153 const start = switch (src_loc.lazy) {2172 const start = switch (src_loc.lazy) {
2154 .switch_capture => case.payload_token.?,2173 .switch_capture => case.payload_token.?,
...@@ -2369,10 +2388,14 @@ pub const LazySrcLoc = struct {...@@ -2369,10 +2388,14 @@ pub const LazySrcLoc = struct {
2369 /// by taking this AST node index offset from the containing base node,2388 /// by taking this AST node index offset from the containing base node,
2370 /// which points to a switch expression AST node. Next, navigate to the operand.2389 /// which points to a switch expression AST node. Next, navigate to the operand.
2371 node_offset_switch_operand: Ast.Node.Offset,2390 node_offset_switch_operand: Ast.Node.Offset,
2372 /// The source location points to the else/`_` prong of a switch expression, found2391 /// The source location points to the else prong of a switch expression, found
2392 /// by taking this AST node index offset from the containing base node,
2393 /// which points to a switch expression AST node. Next, navigate to the else prong.
2394 node_offset_switch_else_prong: Ast.Node.Offset,
2395 /// The source location points to the `_` prong of a switch expression, found
2373 /// by taking this AST node index offset from the containing base node,2396 /// by taking this AST node index offset from the containing base node,
2374 /// which points to a switch expression AST node. Next, navigate to the else/`_` prong.2397 /// which points to a switch expression AST node. Next, navigate to the `_` prong.
2375 node_offset_switch_special_prong: Ast.Node.Offset,2398 node_offset_switch_under_prong: Ast.Node.Offset,
2376 /// The source location points to all the ranges of a switch expression, found2399 /// The source location points to all the ranges of a switch expression, found
2377 /// by taking this AST node index offset from the containing base node,2400 /// by taking this AST node index offset from the containing base node,
2378 /// which points to a switch expression AST node. Next, navigate to any of the2401 /// which points to a switch expression AST node. Next, navigate to any of the
...@@ -2568,7 +2591,8 @@ pub const LazySrcLoc = struct {...@@ -2568,7 +2591,8 @@ pub const LazySrcLoc = struct {
2568 kind: enum(u1) { scalar, multi },2591 kind: enum(u1) { scalar, multi },
2569 index: u31,2592 index: u31,
25702593
2571 pub const special: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32)));2594 pub const special_else: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32)));
2595 pub const special_under: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32) - 1));
2572 };2596 };
25732597
2574 pub const SwitchItemIndex = packed struct(u32) {2598 pub const SwitchItemIndex = packed struct(u32) {
src/print_zir.zig+38-17
...@@ -2087,19 +2087,40 @@ const Writer = struct {...@@ -2087,19 +2087,40 @@ 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.special_prong;
2092 if (special_prong == .none) break :else_prong;
20932091
2092 if (special_prongs.hasElse()) {
2093 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
2094 const capture_text = switch (info.capture) {
2095 .none => "",
2096 .by_val => "by_val ",
2097 .by_ref => "by_ref ",
2098 };
2099 const inline_text = if (info.is_inline) "inline " else "";
2100 extra_index += 1;
2101 const body = self.code.bodySlice(extra_index, info.body_len);
2102 extra_index += body.len;
2103
2104 try stream.writeAll(",\n");
2105 try stream.splatByteAll(' ', self.indent);
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;
2094 var items_len: u32 = 0;2112 var items_len: u32 = 0;
2095 var ranges_len: u32 = 0;2113 var ranges_len: u32 = 0;
2096 if (special_prong == .absorbing_under) {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()) {
2097 items_len = self.code.extra[extra_index];2118 items_len = self.code.extra[extra_index];
2098 extra_index += 1;2119 extra_index += 1;
2099 ranges_len = self.code.extra[extra_index];2120 ranges_len = self.code.extra[extra_index];
2100 extra_index += 1;2121 extra_index += 1;
2101 }2122 }
2102 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));2123 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
2103 extra_index += 1;2124 extra_index += 1;
2104 const items = self.code.refSlice(extra_index, items_len);2125 const items = self.code.refSlice(extra_index, items_len);
2105 extra_index += items_len;2126 extra_index += items_len;
...@@ -2112,12 +2133,12 @@ const Writer = struct {...@@ -2112,12 +2133,12 @@ const Writer = struct {
2112 .by_ref => try stream.writeAll("by_ref "),2133 .by_ref => try stream.writeAll("by_ref "),
2113 }2134 }
2114 if (info.is_inline) try stream.writeAll("inline ");2135 if (info.is_inline) try stream.writeAll("inline ");
2115 switch (special_prong) {
2116 .@"else" => try stream.writeAll("else"),
2117 .under, .absorbing_under => try stream.writeAll("_"),
2118 .none => unreachable,
2119 }
21202136
2137 try stream.writeAll("_");
2138 if (single_item_ref != .none) {
2139 try stream.writeAll(", ");
2140 try self.writeInstRef(stream, single_item_ref);
2141 }
2121 for (items) |item_ref| {2142 for (items) |item_ref| {
2122 try stream.writeAll(", ");2143 try stream.writeAll(", ");
2123 try self.writeInstRef(stream, item_ref);2144 try self.writeInstRef(stream, item_ref);
...@@ -2125,9 +2146,9 @@ const Writer = struct {...@@ -2125,9 +2146,9 @@ const Writer = struct {
21252146
2126 var range_i: usize = 0;2147 var range_i: usize = 0;
2127 while (range_i < ranges_len) : (range_i += 1) {2148 while (range_i < ranges_len) : (range_i += 1) {
2128 const item_first = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));2149 const item_first: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2129 extra_index += 1;2150 extra_index += 1;
2130 const item_last = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));2151 const item_last: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2131 extra_index += 1;2152 extra_index += 1;
21322153
2133 try stream.writeAll(", ");2154 try stream.writeAll(", ");
...@@ -2146,9 +2167,9 @@ const Writer = struct {...@@ -2146,9 +2167,9 @@ const Writer = struct {
2146 const scalar_cases_len = extra.data.bits.scalar_cases_len;2167 const scalar_cases_len = extra.data.bits.scalar_cases_len;
2147 var scalar_i: usize = 0;2168 var scalar_i: usize = 0;
2148 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {2169 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2149 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]);
2150 extra_index += 1;2171 extra_index += 1;
2151 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]);
2152 extra_index += 1;2173 extra_index += 1;
2153 const body = self.code.bodySlice(extra_index, info.body_len);2174 const body = self.code.bodySlice(extra_index, info.body_len);
2154 extra_index += info.body_len;2175 extra_index += info.body_len;
...@@ -2173,7 +2194,7 @@ const Writer = struct {...@@ -2173,7 +2194,7 @@ const Writer = struct {
2173 extra_index += 1;2194 extra_index += 1;
2174 const ranges_len = self.code.extra[extra_index];2195 const ranges_len = self.code.extra[extra_index];
2175 extra_index += 1;2196 extra_index += 1;
2176 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]);
2177 extra_index += 1;2198 extra_index += 1;
2178 const items = self.code.refSlice(extra_index, items_len);2199 const items = self.code.refSlice(extra_index, items_len);
2179 extra_index += items_len;2200 extra_index += items_len;
...@@ -2194,9 +2215,9 @@ const Writer = struct {...@@ -2194,9 +2215,9 @@ const Writer = struct {
21942215
2195 var range_i: usize = 0;2216 var range_i: usize = 0;
2196 while (range_i < ranges_len) : (range_i += 1) {2217 while (range_i < ranges_len) : (range_i += 1) {
2197 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]);
2198 extra_index += 1;2219 extra_index += 1;
2199 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]);
2200 extra_index += 1;2221 extra_index += 1;
22012222
2202 if (range_i != 0 or items.len != 0) {2223 if (range_i != 0 or items.len != 0) {
test/behavior/switch.zig+38-14
...@@ -1075,26 +1075,50 @@ test "switch on 8-bit mod result" {...@@ -1075,26 +1075,50 @@ test "switch on 8-bit mod result" {
1075}1075}
10761076
1077test "switch on non-exhaustive enum" {1077test "switch on non-exhaustive enum" {
1078 const E = enum(u32) {1078 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
1079
1080 const E = enum(u4) {
1079 a,1081 a,
1080 b,1082 b,
1081 c,1083 c,
1082 _,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 }
1083 };1118 };
10841119
1085 var e: E = .a;1120 var e: E = .a;
1086 _ = &e;1121 _ = &e;
1087 switch (e) {1122 try E.doTheTest(e);
1088 .a, .b => {},1123 try comptime E.doTheTest(.a);
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}1124}
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_inline.zig created+27
...@@ -0,0 +1,27 @@
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// backend=stage2
24// target=native
25//
26// :10:16: error: cannot inline '_' prong
27// :18:16: error: cannot inline '_' prong
test/cases/compile_errors/switch_expression-non_exhaustive_unreachable_else.zig created+18
...@@ -0,0 +1,18 @@
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// backend=stage2
16// target=native
17//
18// :10:14: error: unreachable else prong; all explicit cases already handled
test/cases/compile_errors/switching_with_non-exhaustive_enums.zig+1-1
...@@ -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:9: note: '_' prong here42// :29:9: note: '_' prong here
43// :26:5: note: consider using 'else'43// :26:5: note: consider using 'else'