| author | |
| committer | |
| log | 1d9b1c021273037efea6623e669800ad538c8075 |
| tree | 95efb3febc06d875e14cb1df105d686b678ff09f |
| parent | fd9cfc39f53fae7fad4eadf9f1d8943f7e2c03b1 |
Mainly affects ZIR representation of switch_block[_ref]
and special prong (detection) logic for switch.
Adds a new SpecialProng tag 'absorbing_under' that allows
specifying additional explicit tags in a '_' prong which
are respected when checking that every value is handled
during semantic analysis but are not transformed into AIR
and instead 'absorbed' by the '_' branch.10 files changed, 333 insertions(+), 127 deletions(-)
lib/std/zig/Ast.zig+18| ... | @@ -2877,6 +2877,24 @@ pub const full = struct { | ... | @@ -2877,6 +2877,24 @@ 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 | } | ||
| 2880 | }; | 2898 | }; |
| 2881 | 2899 | ||
| 2882 | pub const Asm = struct { | 2900 | pub const Asm = struct { |
lib/std/zig/AstGen.zig+91-59| ... | @@ -7666,6 +7666,7 @@ fn switchExpr( | ... | @@ -7666,6 +7666,7 @@ fn switchExpr( |
| 7666 | var special_node: Ast.Node.OptionalIndex = .none; | 7666 | var special_node: Ast.Node.OptionalIndex = .none; |
| 7667 | var else_src: ?Ast.TokenIndex = null; | 7667 | var else_src: ?Ast.TokenIndex = null; |
| 7668 | var underscore_src: ?Ast.TokenIndex = null; | 7668 | var underscore_src: ?Ast.TokenIndex = null; |
| 7669 | var underscore_node: Ast.Node.OptionalIndex = .none; | ||
| 7669 | for (case_nodes) |case_node| { | 7670 | for (case_nodes) |case_node| { |
| 7670 | const case = tree.fullSwitchCase(case_node).?; | 7671 | const case = tree.fullSwitchCase(case_node).?; |
| 7671 | if (case.payload_token) |payload_token| { | 7672 | if (case.payload_token) |payload_token| { |
| ... | @@ -7686,7 +7687,7 @@ fn switchExpr( | ... | @@ -7686,7 +7687,7 @@ fn switchExpr( |
| 7686 | any_non_inline_capture = true; | 7687 | any_non_inline_capture = true; |
| 7687 | } | 7688 | } |
| 7688 | } | 7689 | } |
| 7689 | // Check for else/`_` prong. | 7690 | // Check for else prong. |
| 7690 | if (case.ast.values.len == 0) { | 7691 | if (case.ast.values.len == 0) { |
| 7691 | const case_src = case.ast.arrow_token - 1; | 7692 | const case_src = case.ast.arrow_token - 1; |
| 7692 | if (else_src) |src| { | 7693 | if (else_src) |src| { |
| ... | @@ -7725,56 +7726,60 @@ fn switchExpr( | ... | @@ -7725,56 +7726,60 @@ fn switchExpr( |
| 7725 | special_prong = .@"else"; | 7726 | special_prong = .@"else"; |
| 7726 | else_src = case_src; | 7727 | else_src = case_src; |
| 7727 | continue; | 7728 | continue; |
| 7728 | } else if (case.ast.values.len == 1 and | ||
| 7729 | tree.nodeTag(case.ast.values[0]) == .identifier and | ||
| 7730 | mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_")) | ||
| 7731 | { | ||
| 7732 | const case_src = case.ast.arrow_token - 1; | ||
| 7733 | if (underscore_src) |src| { | ||
| 7734 | return astgen.failTokNotes( | ||
| 7735 | case_src, | ||
| 7736 | "multiple '_' prongs in switch expression", | ||
| 7737 | .{}, | ||
| 7738 | &[_]u32{ | ||
| 7739 | try astgen.errNoteTok( | ||
| 7740 | src, | ||
| 7741 | "previous '_' prong here", | ||
| 7742 | .{}, | ||
| 7743 | ), | ||
| 7744 | }, | ||
| 7745 | ); | ||
| 7746 | } else if (else_src) |some_else| { | ||
| 7747 | return astgen.failNodeNotes( | ||
| 7748 | node, | ||
| 7749 | "else and '_' prong in switch expression", | ||
| 7750 | .{}, | ||
| 7751 | &[_]u32{ | ||
| 7752 | try astgen.errNoteTok( | ||
| 7753 | some_else, | ||
| 7754 | "else prong here", | ||
| 7755 | .{}, | ||
| 7756 | ), | ||
| 7757 | try astgen.errNoteTok( | ||
| 7758 | case_src, | ||
| 7759 | "'_' prong here", | ||
| 7760 | .{}, | ||
| 7761 | ), | ||
| 7762 | }, | ||
| 7763 | ); | ||
| 7764 | } | ||
| 7765 | if (case.inline_token != null) { | ||
| 7766 | return astgen.failTok(case_src, "cannot inline '_' prong", .{}); | ||
| 7767 | } | ||
| 7768 | special_node = case_node.toOptional(); | ||
| 7769 | special_prong = .under; | ||
| 7770 | underscore_src = case_src; | ||
| 7771 | continue; | ||
| 7772 | } | 7729 | } |
| 7773 | 7730 | ||
| 7731 | // Check for '_' prong. | ||
| 7732 | var found_underscore = false; | ||
| 7774 | for (case.ast.values) |val| { | 7733 | for (case.ast.values) |val| { |
| 7775 | if (tree.nodeTag(val) == .string_literal) | 7734 | switch (tree.nodeTag(val)) { |
| 7776 | return astgen.failNode(val, "cannot switch on strings", .{}); | 7735 | .identifier => if (mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_")) { |
| 7736 | const case_src = case.ast.arrow_token - 1; | ||
| 7737 | if (underscore_src) |src| { | ||
| 7738 | return astgen.failTokNotes( | ||
| 7739 | case_src, | ||
| 7740 | "multiple '_' prongs in switch expression", | ||
| 7741 | .{}, | ||
| 7742 | &[_]u32{ | ||
| 7743 | try astgen.errNoteTok( | ||
| 7744 | src, | ||
| 7745 | "previous '_' prong here", | ||
| 7746 | .{}, | ||
| 7747 | ), | ||
| 7748 | }, | ||
| 7749 | ); | ||
| 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 | } | ||
| 7769 | if (case.inline_token != null) { | ||
| 7770 | return astgen.failTok(case_src, "cannot inline '_' prong", .{}); | ||
| 7771 | } | ||
| 7772 | special_node = case_node.toOptional(); | ||
| 7773 | special_prong = if (case.ast.values.len == 1) .under else .absorbing_under; | ||
| 7774 | underscore_src = case_src; | ||
| 7775 | underscore_node = val.toOptional(); | ||
| 7776 | found_underscore = true; | ||
| 7777 | }, | ||
| 7778 | .string_literal => return astgen.failNode(val, "cannot switch on strings", .{}), | ||
| 7779 | else => {}, | ||
| 7780 | } | ||
| 7777 | } | 7781 | } |
| 7782 | if (found_underscore) continue; | ||
| 7778 | 7783 | ||
| 7779 | if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) { | 7784 | if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) { |
| 7780 | scalar_cases_len += 1; | 7785 | scalar_cases_len += 1; |
| ... | @@ -7938,14 +7943,23 @@ fn switchExpr( | ... | @@ -7938,14 +7943,23 @@ fn switchExpr( |
| 7938 | 7943 | ||
| 7939 | const header_index: u32 = @intCast(payloads.items.len); | 7944 | const header_index: u32 = @intCast(payloads.items.len); |
| 7940 | const body_len_index = if (is_multi_case) blk: { | 7945 | const body_len_index = if (is_multi_case) blk: { |
| 7941 | payloads.items[multi_case_table + multi_case_index] = header_index; | 7946 | if (case_node.toOptional() == special_node) { |
| 7942 | multi_case_index += 1; | 7947 | assert(special_prong == .absorbing_under); |
| 7948 | payloads.items[case_table_start] = header_index; | ||
| 7949 | } else { | ||
| 7950 | payloads.items[multi_case_table + multi_case_index] = header_index; | ||
| 7951 | multi_case_index += 1; | ||
| 7952 | } | ||
| 7943 | try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len | 7953 | try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len |
| 7944 | 7954 | ||
| 7945 | // items | 7955 | // items |
| 7946 | var items_len: u32 = 0; | 7956 | var items_len: u32 = 0; |
| 7947 | for (case.ast.values) |item_node| { | 7957 | for (case.ast.values) |item_node| { |
| 7948 | if (tree.nodeTag(item_node) == .switch_range) continue; | 7958 | if (item_node.toOptional() == underscore_node or |
| 7959 | tree.nodeTag(item_node) == .switch_range) | ||
| 7960 | { | ||
| 7961 | continue; | ||
| 7962 | } | ||
| 7949 | items_len += 1; | 7963 | items_len += 1; |
| 7950 | 7964 | ||
| 7951 | const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item); | 7965 | const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item); |
| ... | @@ -7955,7 +7969,9 @@ fn switchExpr( | ... | @@ -7955,7 +7969,9 @@ fn switchExpr( |
| 7955 | // ranges | 7969 | // ranges |
| 7956 | var ranges_len: u32 = 0; | 7970 | var ranges_len: u32 = 0; |
| 7957 | for (case.ast.values) |range| { | 7971 | for (case.ast.values) |range| { |
| 7958 | if (tree.nodeTag(range) != .switch_range) continue; | 7972 | if (tree.nodeTag(range) != .switch_range) { |
| 7973 | continue; | ||
| 7974 | } | ||
| 7959 | ranges_len += 1; | 7975 | ranges_len += 1; |
| 7960 | 7976 | ||
| 7961 | const first_node, const last_node = tree.nodeData(range).node_and_node; | 7977 | const first_node, const last_node = tree.nodeData(range).node_and_node; |
| ... | @@ -7970,6 +7986,7 @@ fn switchExpr( | ... | @@ -7970,6 +7986,7 @@ fn switchExpr( |
| 7970 | payloads.items[header_index + 1] = ranges_len; | 7986 | payloads.items[header_index + 1] = ranges_len; |
| 7971 | break :blk header_index + 2; | 7987 | break :blk header_index + 2; |
| 7972 | } else if (case_node.toOptional() == special_node) blk: { | 7988 | } else if (case_node.toOptional() == special_node) blk: { |
| 7989 | assert(special_prong != .absorbing_under); | ||
| 7973 | payloads.items[case_table_start] = header_index; | 7990 | payloads.items[case_table_start] = header_index; |
| 7974 | try payloads.resize(gpa, header_index + 1); // body_len | 7991 | try payloads.resize(gpa, header_index + 1); // body_len |
| 7975 | break :blk header_index; | 7992 | break :blk header_index; |
| ... | @@ -8025,15 +8042,13 @@ fn switchExpr( | ... | @@ -8025,15 +8042,13 @@ fn switchExpr( |
| 8025 | try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len + | 8042 | try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len + |
| 8026 | @intFromBool(multi_cases_len != 0) + | 8043 | @intFromBool(multi_cases_len != 0) + |
| 8027 | @intFromBool(any_has_tag_capture) + | 8044 | @intFromBool(any_has_tag_capture) + |
| 8028 | payloads.items.len - case_table_end + | 8045 | payloads.items.len - scratch_top); |
| 8029 | (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len); | ||
| 8030 | 8046 | ||
| 8031 | const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{ | 8047 | const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{ |
| 8032 | .operand = raw_operand, | 8048 | .operand = raw_operand, |
| 8033 | .bits = Zir.Inst.SwitchBlock.Bits{ | 8049 | .bits = Zir.Inst.SwitchBlock.Bits{ |
| 8034 | .has_multi_cases = multi_cases_len != 0, | 8050 | .has_multi_cases = multi_cases_len != 0, |
| 8035 | .has_else = special_prong == .@"else", | 8051 | .special_prong = special_prong, |
| 8036 | .has_under = special_prong == .under, | ||
| 8037 | .any_has_tag_capture = any_has_tag_capture, | 8052 | .any_has_tag_capture = any_has_tag_capture, |
| 8038 | .any_non_inline_capture = any_non_inline_capture, | 8053 | .any_non_inline_capture = any_non_inline_capture, |
| 8039 | .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue, | 8054 | .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue, |
| ... | @@ -8052,13 +8067,30 @@ fn switchExpr( | ... | @@ -8052,13 +8067,30 @@ fn switchExpr( |
| 8052 | const zir_datas = astgen.instructions.items(.data); | 8067 | const zir_datas = astgen.instructions.items(.data); |
| 8053 | zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index; | 8068 | zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index; |
| 8054 | 8069 | ||
| 8055 | for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| { | 8070 | var normal_case_table_start = case_table_start; |
| 8071 | if (special_prong != .none) { | ||
| 8072 | normal_case_table_start += 1; | ||
| 8073 | |||
| 8074 | const start_index = payloads.items[case_table_start]; | ||
| 8056 | var body_len_index = start_index; | 8075 | var body_len_index = start_index; |
| 8057 | var end_index = start_index; | 8076 | var end_index = start_index; |
| 8058 | const table_index = case_table_start + i; | 8077 | if (special_prong == .absorbing_under) { |
| 8059 | if (table_index < scalar_case_table) { | 8078 | body_len_index += 2; |
| 8079 | const items_len = payloads.items[start_index]; | ||
| 8080 | const ranges_len = payloads.items[start_index + 1]; | ||
| 8081 | end_index += 3 + items_len + 2 * ranges_len; | ||
| 8082 | } else { | ||
| 8060 | end_index += 1; | 8083 | end_index += 1; |
| 8061 | } else if (table_index < multi_case_table) { | 8084 | } |
| 8085 | const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]); | ||
| 8086 | end_index += prong_info.body_len; | ||
| 8087 | astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]); | ||
| 8088 | } | ||
| 8089 | for (payloads.items[normal_case_table_start..case_table_end], 0..) |start_index, i| { | ||
| 8090 | var body_len_index = start_index; | ||
| 8091 | var end_index = start_index; | ||
| 8092 | const table_index = normal_case_table_start + i; | ||
| 8093 | if (table_index < multi_case_table) { | ||
| 8062 | body_len_index += 1; | 8094 | body_len_index += 1; |
| 8063 | end_index += 2; | 8095 | end_index += 2; |
| 8064 | } else { | 8096 | } else { |
lib/std/zig/Zir.zig+41-19| ... | @@ -3226,8 +3226,15 @@ pub const Inst = struct { | ... | @@ -3226,8 +3226,15 @@ pub const Inst = struct { |
| 3226 | 3226 | ||
| 3227 | /// 0. multi_cases_len: u32 // If has_multi_cases is set. | 3227 | /// 0. multi_cases_len: u32 // If has_multi_cases is set. |
| 3228 | /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture. | 3228 | /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture. |
| 3229 | /// 2. else_body { // If has_else or has_under is set. | 3229 | /// 2. else_body { // If special_prong != .none |
| 3230 | /// items_len: u32, // If special_prong == .absorbing_under | ||
| 3231 | /// ranges_len: u32, // If special_prong == .absorbing_under | ||
| 3230 | /// info: ProngInfo, | 3232 | /// info: ProngInfo, |
| 3233 | /// item: Ref, // for every items_len | ||
| 3234 | /// ranges: { // for every ranges_len | ||
| 3235 | /// item_first: Ref, | ||
| 3236 | /// item_last: Ref, | ||
| 3237 | /// } | ||
| 3231 | /// body member Index for every info.body_len | 3238 | /// body member Index for every info.body_len |
| 3232 | /// } | 3239 | /// } |
| 3233 | /// 3. scalar_cases: { // for every scalar_cases_len | 3240 | /// 3. scalar_cases: { // for every scalar_cases_len |
| ... | @@ -3239,7 +3246,7 @@ pub const Inst = struct { | ... | @@ -3239,7 +3246,7 @@ pub const Inst = struct { |
| 3239 | /// items_len: u32, | 3246 | /// items_len: u32, |
| 3240 | /// ranges_len: u32, | 3247 | /// ranges_len: u32, |
| 3241 | /// info: ProngInfo, | 3248 | /// info: ProngInfo, |
| 3242 | /// item: Ref // for every items_len | 3249 | /// item: Ref, // for every items_len |
| 3243 | /// ranges: { // for every ranges_len | 3250 | /// ranges: { // for every ranges_len |
| 3244 | /// item_first: Ref, | 3251 | /// item_first: Ref, |
| 3245 | /// item_last: Ref, | 3252 | /// item_last: Ref, |
| ... | @@ -3275,10 +3282,8 @@ pub const Inst = struct { | ... | @@ -3275,10 +3282,8 @@ pub const Inst = struct { |
| 3275 | pub const Bits = packed struct(u32) { | 3282 | pub const Bits = packed struct(u32) { |
| 3276 | /// If true, one or more prongs have multiple items. | 3283 | /// If true, one or more prongs have multiple items. |
| 3277 | has_multi_cases: bool, | 3284 | has_multi_cases: bool, |
| 3278 | /// If true, there is an else prong. This is mutually exclusive with `has_under`. | 3285 | /// Information about the special prong. |
| 3279 | has_else: bool, | 3286 | special_prong: SpecialProng, |
| 3280 | /// If true, there is an underscore prong. This is mutually exclusive with `has_else`. | ||
| 3281 | has_under: bool, | ||
| 3282 | /// If true, at least one prong has an inline tag capture. | 3287 | /// If true, at least one prong has an inline tag capture. |
| 3283 | any_has_tag_capture: bool, | 3288 | any_has_tag_capture: bool, |
| 3284 | /// If true, at least one prong has a capture which may not | 3289 | /// If true, at least one prong has a capture which may not |
| ... | @@ -3288,17 +3293,6 @@ pub const Inst = struct { | ... | @@ -3288,17 +3293,6 @@ pub const Inst = struct { |
| 3288 | scalar_cases_len: ScalarCasesLen, | 3293 | scalar_cases_len: ScalarCasesLen, |
| 3289 | 3294 | ||
| 3290 | pub const ScalarCasesLen = u26; | 3295 | pub const ScalarCasesLen = u26; |
| 3291 | |||
| 3292 | pub fn specialProng(bits: Bits) SpecialProng { | ||
| 3293 | const has_else: u2 = @intFromBool(bits.has_else); | ||
| 3294 | const has_under: u2 = @intFromBool(bits.has_under); | ||
| 3295 | return switch ((has_else << 1) | has_under) { | ||
| 3296 | 0b00 => .none, | ||
| 3297 | 0b01 => .under, | ||
| 3298 | 0b10 => .@"else", | ||
| 3299 | 0b11 => unreachable, | ||
| 3300 | }; | ||
| 3301 | } | ||
| 3302 | }; | 3296 | }; |
| 3303 | 3297 | ||
| 3304 | pub const MultiProng = struct { | 3298 | pub const MultiProng = struct { |
| ... | @@ -3874,7 +3868,18 @@ pub const Inst = struct { | ... | @@ -3874,7 +3868,18 @@ pub const Inst = struct { |
| 3874 | }; | 3868 | }; |
| 3875 | }; | 3869 | }; |
| 3876 | 3870 | ||
| 3877 | pub const SpecialProng = enum { none, @"else", under }; | 3871 | pub const SpecialProng = enum(u2) { |
| 3872 | none, | ||
| 3873 | /// Simple else prong. | ||
| 3874 | /// `else => {}` | ||
| 3875 | @"else", | ||
| 3876 | /// Simple '_' prong. | ||
| 3877 | /// `_ => {}` | ||
| 3878 | under, | ||
| 3879 | /// '_' prong with additional items. | ||
| 3880 | /// `a, _, b => {}` | ||
| 3881 | absorbing_under, | ||
| 3882 | }; | ||
| 3878 | 3883 | ||
| 3879 | pub const DeclIterator = struct { | 3884 | pub const DeclIterator = struct { |
| 3880 | extra_index: u32, | 3885 | extra_index: u32, |
| ... | @@ -4718,7 +4723,7 @@ fn findTrackableSwitch( | ... | @@ -4718,7 +4723,7 @@ fn findTrackableSwitch( |
| 4718 | } | 4723 | } |
| 4719 | 4724 | ||
| 4720 | const has_special = switch (kind) { | 4725 | const has_special = switch (kind) { |
| 4721 | .normal => extra.data.bits.specialProng() != .none, | 4726 | .normal => extra.data.bits.special_prong != .none, |
| 4722 | .err_union => has_special: { | 4727 | .err_union => has_special: { |
| 4723 | // Handle `non_err_body` first. | 4728 | // Handle `non_err_body` first. |
| 4724 | const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]); | 4729 | const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]); |
| ... | @@ -4733,6 +4738,23 @@ fn findTrackableSwitch( | ... | @@ -4733,6 +4738,23 @@ fn findTrackableSwitch( |
| 4733 | }; | 4738 | }; |
| 4734 | 4739 | ||
| 4735 | if (has_special) { | 4740 | if (has_special) { |
| 4741 | if (kind == .normal) { | ||
| 4742 | if (extra.data.bits.special_prong == .absorbing_under) { | ||
| 4743 | const items_len = zir.extra[extra_index]; | ||
| 4744 | extra_index += 1; | ||
| 4745 | const ranges_len = zir.extra[extra_index]; | ||
| 4746 | extra_index += 1; | ||
| 4747 | const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]); | ||
| 4748 | extra_index += 1; | ||
| 4749 | |||
| 4750 | extra_index += items_len + ranges_len * 2; | ||
| 4751 | |||
| 4752 | const body = zir.bodySlice(extra_index, prong_info.body_len); | ||
| 4753 | extra_index += body.len; | ||
| 4754 | |||
| 4755 | try zir.findTrackableBody(gpa, contents, defers, body); | ||
| 4756 | } | ||
| 4757 | } | ||
| 4736 | const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]); | 4758 | const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]); |
| 4737 | extra_index += 1; | 4759 | extra_index += 1; |
| 4738 | const body = zir.bodySlice(extra_index, prong_info.body_len); | 4760 | const body = zir.bodySlice(extra_index, prong_info.body_len); |
src/Sema.zig+52-11| ... | @@ -11335,7 +11335,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11335,7 +11335,10 @@ 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); | 11335 | var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); |
| 11336 | defer case_vals.deinit(gpa); | 11336 | defer case_vals.deinit(gpa); |
| 11337 | 11337 | ||
| 11338 | const special_prong = extra.data.bits.specialProng(); | 11338 | var absorbed_items: []const Zir.Inst.Ref = &.{}; |
| 11339 | var absorbed_ranges: []const Zir.Inst.Ref = &.{}; | ||
| 11340 | |||
| 11341 | const special_prong = extra.data.bits.special_prong; | ||
| 11339 | const special: SpecialProng = switch (special_prong) { | 11342 | const special: SpecialProng = switch (special_prong) { |
| 11340 | .none => .{ | 11343 | .none => .{ |
| 11341 | .body = &.{}, | 11344 | .body = &.{}, |
| ... | @@ -11355,6 +11358,26 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11355,6 +11358,26 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11355 | .has_tag_capture = info.has_tag_capture, | 11358 | .has_tag_capture = info.has_tag_capture, |
| 11356 | }; | 11359 | }; |
| 11357 | }, | 11360 | }, |
| 11361 | .absorbing_under => blk: { | ||
| 11362 | var extra_index = header_extra_index; | ||
| 11363 | const items_len = sema.code.extra[extra_index]; | ||
| 11364 | extra_index += 1; | ||
| 11365 | const ranges_len = sema.code.extra[extra_index]; | ||
| 11366 | extra_index += 1; | ||
| 11367 | const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); | ||
| 11368 | extra_index += 1; | ||
| 11369 | absorbed_items = sema.code.refSlice(extra_index, items_len); | ||
| 11370 | extra_index += items_len; | ||
| 11371 | absorbed_ranges = sema.code.refSlice(extra_index, ranges_len * 2); | ||
| 11372 | extra_index += ranges_len * 2; | ||
| 11373 | break :blk .{ | ||
| 11374 | .body = sema.code.bodySlice(extra_index, info.body_len), | ||
| 11375 | .end = extra_index + info.body_len, | ||
| 11376 | .capture = info.capture, | ||
| 11377 | .is_inline = info.is_inline, | ||
| 11378 | .has_tag_capture = info.has_tag_capture, | ||
| 11379 | }; | ||
| 11380 | }, | ||
| 11358 | }; | 11381 | }; |
| 11359 | 11382 | ||
| 11360 | // Duplicate checking variables later also used for `inline else`. | 11383 | // Duplicate checking variables later also used for `inline else`. |
| ... | @@ -11375,7 +11398,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11375,7 +11398,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11375 | var else_error_ty: ?Type = null; | 11398 | var else_error_ty: ?Type = null; |
| 11376 | 11399 | ||
| 11377 | // Validate usage of '_' prongs. | 11400 | // Validate usage of '_' prongs. |
| 11378 | if (special_prong == .under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) { | 11401 | if ((special_prong == .under or special_prong == .absorbing_under) and |
| 11402 | !raw_operand_ty.isNonexhaustiveEnum(zcu)) | ||
| 11403 | { | ||
| 11379 | const msg = msg: { | 11404 | const msg = msg: { |
| 11380 | const msg = try sema.errMsg( | 11405 | const msg = try sema.errMsg( |
| 11381 | src, | 11406 | src, |
| ... | @@ -11409,6 +11434,22 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11409,6 +11434,22 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11409 | @memset(seen_enum_fields, null); | 11434 | @memset(seen_enum_fields, null); |
| 11410 | // `range_set` is used for non-exhaustive enum values that do not correspond to any tags. | 11435 | // `range_set` is used for non-exhaustive enum values that do not correspond to any tags. |
| 11411 | 11436 | ||
| 11437 | for (absorbed_items, 0..) |item_ref, item_i| { | ||
| 11438 | _ = try sema.validateSwitchItemEnum( | ||
| 11439 | block, | ||
| 11440 | seen_enum_fields, | ||
| 11441 | &range_set, | ||
| 11442 | item_ref, | ||
| 11443 | cond_ty, | ||
| 11444 | block.src(.{ .switch_case_item = .{ | ||
| 11445 | .switch_node_offset = src_node_offset, | ||
| 11446 | .case_idx = .special, | ||
| 11447 | .item_idx = .{ .kind = .single, .index = @intCast(item_i) }, | ||
| 11448 | } }), | ||
| 11449 | ); | ||
| 11450 | } | ||
| 11451 | try sema.validateSwitchNoRange(block, @intCast(absorbed_ranges.len), cond_ty, src_node_offset); | ||
| 11452 | |||
| 11412 | var extra_index: usize = special.end; | 11453 | var extra_index: usize = special.end; |
| 11413 | { | 11454 | { |
| 11414 | var scalar_i: u32 = 0; | 11455 | var scalar_i: u32 = 0; |
| ... | @@ -11692,7 +11733,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11692,7 +11733,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11692 | ); | 11733 | ); |
| 11693 | } | 11734 | } |
| 11694 | }, | 11735 | }, |
| 11695 | .under, .none => { | 11736 | .under, .absorbing_under, .none => { |
| 11696 | if (true_count + false_count < 2) { | 11737 | if (true_count + false_count < 2) { |
| 11697 | return sema.fail( | 11738 | return sema.fail( |
| 11698 | block, | 11739 | block, |
| ... | @@ -11892,7 +11933,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11892,7 +11933,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11892 | special.capture, | 11933 | special.capture, |
| 11893 | block.src(.{ .switch_capture = .{ | 11934 | block.src(.{ .switch_capture = .{ |
| 11894 | .switch_node_offset = src_node_offset, | 11935 | .switch_node_offset = src_node_offset, |
| 11895 | .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, | 11936 | .case_idx = .special, |
| 11896 | } }), | 11937 | } }), |
| 11897 | undefined, // case_vals may be undefined for special prongs | 11938 | undefined, // case_vals may be undefined for special prongs |
| 11898 | .none, | 11939 | .none, |
| ... | @@ -12344,7 +12385,7 @@ fn analyzeSwitchRuntimeBlock( | ... | @@ -12344,7 +12385,7 @@ fn analyzeSwitchRuntimeBlock( |
| 12344 | special.capture, | 12385 | special.capture, |
| 12345 | child_block.src(.{ .switch_capture = .{ | 12386 | child_block.src(.{ .switch_capture = .{ |
| 12346 | .switch_node_offset = switch_node_offset, | 12387 | .switch_node_offset = switch_node_offset, |
| 12347 | .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, | 12388 | .case_idx = .special, |
| 12348 | } }), | 12389 | } }), |
| 12349 | &.{item_ref}, | 12390 | &.{item_ref}, |
| 12350 | item_ref, | 12391 | item_ref, |
| ... | @@ -12399,7 +12440,7 @@ fn analyzeSwitchRuntimeBlock( | ... | @@ -12399,7 +12440,7 @@ fn analyzeSwitchRuntimeBlock( |
| 12399 | special.capture, | 12440 | special.capture, |
| 12400 | child_block.src(.{ .switch_capture = .{ | 12441 | child_block.src(.{ .switch_capture = .{ |
| 12401 | .switch_node_offset = switch_node_offset, | 12442 | .switch_node_offset = switch_node_offset, |
| 12402 | .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, | 12443 | .case_idx = .special, |
| 12403 | } }), | 12444 | } }), |
| 12404 | &.{item_ref}, | 12445 | &.{item_ref}, |
| 12405 | item_ref, | 12446 | item_ref, |
| ... | @@ -12439,7 +12480,7 @@ fn analyzeSwitchRuntimeBlock( | ... | @@ -12439,7 +12480,7 @@ fn analyzeSwitchRuntimeBlock( |
| 12439 | special.capture, | 12480 | special.capture, |
| 12440 | child_block.src(.{ .switch_capture = .{ | 12481 | child_block.src(.{ .switch_capture = .{ |
| 12441 | .switch_node_offset = switch_node_offset, | 12482 | .switch_node_offset = switch_node_offset, |
| 12442 | .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, | 12483 | .case_idx = .special, |
| 12443 | } }), | 12484 | } }), |
| 12444 | &.{item_ref}, | 12485 | &.{item_ref}, |
| 12445 | item_ref, | 12486 | item_ref, |
| ... | @@ -12476,7 +12517,7 @@ fn analyzeSwitchRuntimeBlock( | ... | @@ -12476,7 +12517,7 @@ fn analyzeSwitchRuntimeBlock( |
| 12476 | special.capture, | 12517 | special.capture, |
| 12477 | child_block.src(.{ .switch_capture = .{ | 12518 | child_block.src(.{ .switch_capture = .{ |
| 12478 | .switch_node_offset = switch_node_offset, | 12519 | .switch_node_offset = switch_node_offset, |
| 12479 | .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, | 12520 | .case_idx = .special, |
| 12480 | } }), | 12521 | } }), |
| 12481 | &.{.bool_true}, | 12522 | &.{.bool_true}, |
| 12482 | .bool_true, | 12523 | .bool_true, |
| ... | @@ -12511,7 +12552,7 @@ fn analyzeSwitchRuntimeBlock( | ... | @@ -12511,7 +12552,7 @@ fn analyzeSwitchRuntimeBlock( |
| 12511 | special.capture, | 12552 | special.capture, |
| 12512 | child_block.src(.{ .switch_capture = .{ | 12553 | child_block.src(.{ .switch_capture = .{ |
| 12513 | .switch_node_offset = switch_node_offset, | 12554 | .switch_node_offset = switch_node_offset, |
| 12514 | .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, | 12555 | .case_idx = .special, |
| 12515 | } }), | 12556 | } }), |
| 12516 | &.{.bool_false}, | 12557 | &.{.bool_false}, |
| 12517 | .bool_false, | 12558 | .bool_false, |
| ... | @@ -12571,7 +12612,7 @@ fn analyzeSwitchRuntimeBlock( | ... | @@ -12571,7 +12612,7 @@ fn analyzeSwitchRuntimeBlock( |
| 12571 | special.capture, | 12612 | special.capture, |
| 12572 | child_block.src(.{ .switch_capture = .{ | 12613 | child_block.src(.{ .switch_capture = .{ |
| 12573 | .switch_node_offset = switch_node_offset, | 12614 | .switch_node_offset = switch_node_offset, |
| 12574 | .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, | 12615 | .case_idx = .special, |
| 12575 | } }), | 12616 | } }), |
| 12576 | undefined, // case_vals may be undefined for special prongs | 12617 | undefined, // case_vals may be undefined for special prongs |
| 12577 | .none, | 12618 | .none, |
| ... | @@ -12836,7 +12877,7 @@ fn resolveSwitchComptime( | ... | @@ -12836,7 +12877,7 @@ fn resolveSwitchComptime( |
| 12836 | special.capture, | 12877 | special.capture, |
| 12837 | child_block.src(.{ .switch_capture = .{ | 12878 | child_block.src(.{ .switch_capture = .{ |
| 12838 | .switch_node_offset = switch_node_offset, | 12879 | .switch_node_offset = switch_node_offset, |
| 12839 | .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special, | 12880 | .case_idx = .special, |
| 12840 | } }), | 12881 | } }), |
| 12841 | undefined, // case_vals may be undefined for special prongs | 12882 | undefined, // case_vals may be undefined for special prongs |
| 12842 | if (special.is_inline) cond_operand else .none, | 12883 | if (special.is_inline) cond_operand else .none, |
src/Zcu.zig+26-21| ... | @@ -1684,13 +1684,13 @@ pub const SrcLoc = struct { | ... | @@ -1684,13 +1684,13 @@ pub const SrcLoc = struct { |
| 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 | const is_special = (case.ast.values.len == 0) or | 1687 | if (case.isSpecial(tree)) |special_node| { |
| 1688 | (case.ast.values.len == 1 and | 1688 | return tree.tokensToSpan( |
| 1689 | tree.nodeTag(case.ast.values[0]) == .identifier and | 1689 | tree.firstToken(case_node), |
| 1690 | mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_")); | 1690 | tree.lastToken(case_node), |
| 1691 | if (!is_special) continue; | 1691 | tree.nodeMainToken(special_node.unwrap() orelse case_node), |
| 1692 | 1692 | ); | |
| 1693 | return tree.nodeToSpan(case_node); | 1693 | } |
| 1694 | } else unreachable; | 1694 | } else unreachable; |
| 1695 | }, | 1695 | }, |
| 1696 | 1696 | ||
| ... | @@ -1701,11 +1701,9 @@ pub const SrcLoc = struct { | ... | @@ -1701,11 +1701,9 @@ pub const SrcLoc = struct { |
| 1701 | const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index); | 1701 | const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index); |
| 1702 | for (case_nodes) |case_node| { | 1702 | for (case_nodes) |case_node| { |
| 1703 | const case = tree.fullSwitchCase(case_node).?; | 1703 | const case = tree.fullSwitchCase(case_node).?; |
| 1704 | const is_special = (case.ast.values.len == 0) or | 1704 | if (case.isSpecial(tree)) |maybe_else| { |
| 1705 | (case.ast.values.len == 1 and | 1705 | if (maybe_else == .none) continue; |
| 1706 | tree.nodeTag(case.ast.values[0]) == .identifier and | 1706 | } |
| 1707 | mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_")); | ||
| 1708 | if (is_special) continue; | ||
| 1709 | 1707 | ||
| 1710 | for (case.ast.values) |item_node| { | 1708 | for (case.ast.values) |item_node| { |
| 1711 | if (tree.nodeTag(item_node) == .switch_range) { | 1709 | if (tree.nodeTag(item_node) == .switch_range) { |
| ... | @@ -2111,17 +2109,21 @@ pub const SrcLoc = struct { | ... | @@ -2111,17 +2109,21 @@ pub const SrcLoc = struct { |
| 2111 | 2109 | ||
| 2112 | var multi_i: u32 = 0; | 2110 | var multi_i: u32 = 0; |
| 2113 | var scalar_i: u32 = 0; | 2111 | var scalar_i: u32 = 0; |
| 2112 | var found_special = false; | ||
| 2113 | var underscore_node: Ast.Node.OptionalIndex = .none; | ||
| 2114 | const case = for (case_nodes) |case_node| { | 2114 | const case = for (case_nodes) |case_node| { |
| 2115 | const case = tree.fullSwitchCase(case_node).?; | 2115 | const case = tree.fullSwitchCase(case_node).?; |
| 2116 | const is_special = special: { | 2116 | const is_special = special: { |
| 2117 | if (case.ast.values.len == 0) break :special true; | 2117 | if (found_special) break :special false; |
| 2118 | if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) == .identifier) { | 2118 | if (case.isSpecial(tree)) |special_node| { |
| 2119 | break :special mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_"); | 2119 | underscore_node = special_node; |
| 2120 | found_special = true; | ||
| 2121 | break :special true; | ||
| 2120 | } | 2122 | } |
| 2121 | break :special false; | 2123 | break :special false; |
| 2122 | }; | 2124 | }; |
| 2123 | if (is_special) { | 2125 | if (is_special) { |
| 2124 | if (want_case_idx.isSpecial()) { | 2126 | if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special) { |
| 2125 | break case; | 2127 | break case; |
| 2126 | } | 2128 | } |
| 2127 | continue; | 2129 | continue; |
| ... | @@ -2171,7 +2173,11 @@ pub const SrcLoc = struct { | ... | @@ -2171,7 +2173,11 @@ pub const SrcLoc = struct { |
| 2171 | .single => { | 2173 | .single => { |
| 2172 | var item_i: u32 = 0; | 2174 | var item_i: u32 = 0; |
| 2173 | for (case.ast.values) |item_node| { | 2175 | for (case.ast.values) |item_node| { |
| 2174 | if (tree.nodeTag(item_node) == .switch_range) continue; | 2176 | if (item_node.toOptional() == underscore_node or |
| 2177 | tree.nodeTag(item_node) == .switch_range) | ||
| 2178 | { | ||
| 2179 | continue; | ||
| 2180 | } | ||
| 2175 | if (item_i != want_item.index) { | 2181 | if (item_i != want_item.index) { |
| 2176 | item_i += 1; | 2182 | item_i += 1; |
| 2177 | continue; | 2183 | continue; |
| ... | @@ -2182,7 +2188,9 @@ pub const SrcLoc = struct { | ... | @@ -2182,7 +2188,9 @@ pub const SrcLoc = struct { |
| 2182 | .range => { | 2188 | .range => { |
| 2183 | var range_i: u32 = 0; | 2189 | var range_i: u32 = 0; |
| 2184 | for (case.ast.values) |item_node| { | 2190 | for (case.ast.values) |item_node| { |
| 2185 | if (tree.nodeTag(item_node) != .switch_range) continue; | 2191 | if (tree.nodeTag(item_node) != .switch_range) { |
| 2192 | continue; | ||
| 2193 | } | ||
| 2186 | if (range_i != want_item.index) { | 2194 | if (range_i != want_item.index) { |
| 2187 | range_i += 1; | 2195 | range_i += 1; |
| 2188 | continue; | 2196 | continue; |
| ... | @@ -2561,9 +2569,6 @@ pub const LazySrcLoc = struct { | ... | @@ -2561,9 +2569,6 @@ pub const LazySrcLoc = struct { |
| 2561 | index: u31, | 2569 | index: u31, |
| 2562 | 2570 | ||
| 2563 | pub const special: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32))); | 2571 | pub const special: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32))); |
| 2564 | pub fn isSpecial(idx: SwitchCaseIndex) bool { | ||
| 2565 | return @as(u32, @bitCast(idx)) == @as(u32, @bitCast(special)); | ||
| 2566 | } | ||
| 2567 | }; | 2572 | }; |
| 2568 | 2573 | ||
| 2569 | pub const SwitchItemIndex = packed struct(u32) { | 2574 | pub const SwitchItemIndex = packed struct(u32) { |
src/print_zir.zig+45-15| ... | @@ -2088,27 +2088,57 @@ const Writer = struct { | ... | @@ -2088,27 +2088,57 @@ const Writer = struct { |
| 2088 | self.indent += 2; | 2088 | self.indent += 2; |
| 2089 | 2089 | ||
| 2090 | else_prong: { | 2090 | else_prong: { |
| 2091 | const special_prong = extra.data.bits.specialProng(); | 2091 | const special_prong = extra.data.bits.special_prong; |
| 2092 | const prong_name = switch (special_prong) { | 2092 | if (special_prong == .none) break :else_prong; |
| 2093 | .@"else" => "else", | ||
| 2094 | .under => "_", | ||
| 2095 | else => break :else_prong, | ||
| 2096 | }; | ||
| 2097 | 2093 | ||
| 2094 | var items_len: u32 = 0; | ||
| 2095 | var ranges_len: u32 = 0; | ||
| 2096 | if (special_prong == .absorbing_under) { | ||
| 2097 | items_len = self.code.extra[extra_index]; | ||
| 2098 | extra_index += 1; | ||
| 2099 | ranges_len = self.code.extra[extra_index]; | ||
| 2100 | extra_index += 1; | ||
| 2101 | } | ||
| 2098 | const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index])); | 2102 | const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index])); |
| 2099 | const capture_text = switch (info.capture) { | ||
| 2100 | .none => "", | ||
| 2101 | .by_val => "by_val ", | ||
| 2102 | .by_ref => "by_ref ", | ||
| 2103 | }; | ||
| 2104 | const inline_text = if (info.is_inline) "inline " else ""; | ||
| 2105 | extra_index += 1; | 2103 | extra_index += 1; |
| 2106 | const body = self.code.bodySlice(extra_index, info.body_len); | 2104 | const items = self.code.refSlice(extra_index, items_len); |
| 2107 | extra_index += body.len; | 2105 | extra_index += items_len; |
| 2108 | 2106 | ||
| 2109 | try stream.writeAll(",\n"); | 2107 | try stream.writeAll(",\n"); |
| 2110 | try stream.splatByteAll(' ', self.indent); | 2108 | try stream.splatByteAll(' ', self.indent); |
| 2111 | try stream.print("{s}{s}{s} => ", .{ capture_text, inline_text, prong_name }); | 2109 | switch (info.capture) { |
| 2110 | .none => {}, | ||
| 2111 | .by_val => try stream.writeAll("by_val "), | ||
| 2112 | .by_ref => try stream.writeAll("by_ref "), | ||
| 2113 | } | ||
| 2114 | 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 | } | ||
| 2120 | |||
| 2121 | for (items) |item_ref| { | ||
| 2122 | try stream.writeAll(", "); | ||
| 2123 | try self.writeInstRef(stream, item_ref); | ||
| 2124 | } | ||
| 2125 | |||
| 2126 | var range_i: usize = 0; | ||
| 2127 | while (range_i < ranges_len) : (range_i += 1) { | ||
| 2128 | const item_first = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); | ||
| 2129 | extra_index += 1; | ||
| 2130 | const item_last = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); | ||
| 2131 | extra_index += 1; | ||
| 2132 | |||
| 2133 | try stream.writeAll(", "); | ||
| 2134 | try self.writeInstRef(stream, item_first); | ||
| 2135 | try stream.writeAll("..."); | ||
| 2136 | try self.writeInstRef(stream, item_last); | ||
| 2137 | } | ||
| 2138 | |||
| 2139 | const body = self.code.bodySlice(extra_index, info.body_len); | ||
| 2140 | extra_index += info.body_len; | ||
| 2141 | try stream.writeAll(" => "); | ||
| 2112 | try self.writeBracedBody(stream, body); | 2142 | try self.writeBracedBody(stream, body); |
| 2113 | } | 2143 | } |
| 2114 | 2144 |
test/behavior/switch.zig+25| ... | @@ -1073,3 +1073,28 @@ test "switch on 8-bit mod result" { | ... | @@ -1073,3 +1073,28 @@ test "switch on 8-bit mod result" { |
| 1073 | else => unreachable, | 1073 | else => unreachable, |
| 1074 | } | 1074 | } |
| 1075 | } | 1075 | } |
| 1076 | |||
| 1077 | test "switch on non-exhaustive enum" { | ||
| 1078 | const E = enum(u32) { | ||
| 1079 | a, | ||
| 1080 | b, | ||
| 1081 | c, | ||
| 1082 | _, | ||
| 1083 | }; | ||
| 1084 | |||
| 1085 | var e: E = .a; | ||
| 1086 | _ = &e; | ||
| 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 | } |
test/cases/compile_errors/switch_expression-non_exhaustive_absorbing.zig created+33| ... | @@ -0,0 +1,33 @@ | ||
| 1 | const E = enum(u8) { | ||
| 2 | a, | ||
| 3 | b, | ||
| 4 | _, | ||
| 5 | }; | ||
| 6 | const U = union(E) { | ||
| 7 | a: i32, | ||
| 8 | b: u32, | ||
| 9 | }; | ||
| 10 | pub export fn entry1() void { | ||
| 11 | const e: E = .b; | ||
| 12 | switch (e) { // error: switch not handling the tag `b` | ||
| 13 | .a, _ => {}, | ||
| 14 | } | ||
| 15 | } | ||
| 16 | pub export fn entry2() void { | ||
| 17 | const u = U{ .a = 2 }; | ||
| 18 | switch (u) { // error: `_` prong not allowed when switching on tagged union | ||
| 19 | .a => {}, | ||
| 20 | .b, _ => {}, | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | // error | ||
| 25 | // backend=stage2 | ||
| 26 | // target=native | ||
| 27 | // | ||
| 28 | // :12:5: error: switch must handle all possibilities | ||
| 29 | // :3:5: note: unhandled enumeration value: 'b' | ||
| 30 | // :1:11: note: enum 'tmp.E' declared here | ||
| 31 | // :18:5: error: '_' prong only allowed when switching on non-exhaustive enums | ||
| 32 | // :20:13: note: '_' prong here | ||
| 33 | // :18:5: note: consider using 'else' | ||
test/cases/compile_errors/switching_with_exhaustive_enum_has___prong_.zig+1-1| ... | @@ -16,5 +16,5 @@ pub export fn entry() void { | ... | @@ -16,5 +16,5 @@ pub export fn entry() void { |
| 16 | // target=native | 16 | // target=native |
| 17 | // | 17 | // |
| 18 | // :7:5: error: '_' prong only allowed when switching on non-exhaustive enums | 18 | // :7:5: error: '_' prong only allowed when switching on non-exhaustive enums |
| 19 | // :10:11: note: '_' prong here | 19 | // :10:9: note: '_' prong here |
| 20 | // :7:5: note: consider using 'else' | 20 | // :7:5: note: consider using 'else' |
test/cases/compile_errors/switching_with_non-exhaustive_enums.zig+1-1| ... | @@ -39,5 +39,5 @@ pub export fn entry3() void { | ... | @@ -39,5 +39,5 @@ pub export fn entry3() void { |
| 39 | // :1:11: note: enum 'tmp.E' declared here | 39 | // :1:11: note: enum 'tmp.E' declared here |
| 40 | // :19:5: error: switch on non-exhaustive enum must include 'else' or '_' prong | 40 | // :19:5: error: switch on non-exhaustive enum must include 'else' or '_' prong |
| 41 | // :26:5: error: '_' prong only allowed when switching on non-exhaustive enums | 41 | // :26:5: error: '_' prong only allowed when switching on non-exhaustive enums |
| 42 | // :29:11: note: '_' prong here | 42 | // :29:9: note: '_' prong here |
| 43 | // :26:5: note: consider using 'else' | 43 | // :26:5: note: consider using 'else' |