authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-09 13:54:27-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-01-09 13:54:27-08:00
logacca16c8cee4529bcb11c150c2c99b7de32ce21f
tree78900c056830d99f6dfaa849faf23df8d4e53032
parentdbdee2d53cf22be8bcc9031c1c15a58ce530b131
parent67d7d7b5a79cd709749f7435e1c3e4ef2e9150be
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #18173 from dweiller/switch-err-union

Special-case switching on error union capture

14 files changed, 2412 insertions(+), 301 deletions(-)

doc/langref.html.in+26
...@@ -6801,6 +6801,32 @@ test "peer type resolution: *const T and ?*T" {...@@ -6801,6 +6801,32 @@ test "peer type resolution: *const T and ?*T" {
6801 try expect(a == b);6801 try expect(a == b);
6802 try expect(b == a);6802 try expect(b == a);
6803}6803}
6804
6805test "peer type resolution: error union switch" {
6806 // The non-error and error cases are only peers if the error case is just a switch expression;
6807 // the pattern `if (x) {...} else |err| blk: { switch (err) {...} }` does not consider the
6808 // non-error and error case to be peers.
6809 var a: error{ A, B, C }!u32 = 0;
6810 _ = &a;
6811 const b = if (a) |x|
6812 x + 3
6813 else |err| switch (err) {
6814 error.A => 0,
6815 error.B => 1,
6816 error.C => null,
6817 };
6818 try expect(@TypeOf(b) == ?u32);
6819
6820 // The non-error and error cases are only peers if the error case is just a switch expression;
6821 // the pattern `x catch |err| blk: { switch (err) {...} }` does not consider the unwrapped `x`
6822 // and error case to be peers.
6823 const c = a catch |err| switch (err) {
6824 error.A => 0,
6825 error.B => 1,
6826 error.C => null,
6827 };
6828 try expect(@TypeOf(c) == ?u32);
6829}
6804 {#code_end#}6830 {#code_end#}
6805 {#header_close#}6831 {#header_close#}
6806 {#header_close#}6832 {#header_close#}
src/AstGen.zig+556-6
...@@ -93,6 +93,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {...@@ -93,6 +93,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
93 Zir.Inst.Call.Flags,93 Zir.Inst.Call.Flags,
94 Zir.Inst.BuiltinCall.Flags,94 Zir.Inst.BuiltinCall.Flags,
95 Zir.Inst.SwitchBlock.Bits,95 Zir.Inst.SwitchBlock.Bits,
96 Zir.Inst.SwitchBlockErrUnion.Bits,
96 Zir.Inst.FuncFancy.Bits,97 Zir.Inst.FuncFancy.Bits,
97 => @bitCast(@field(extra, field.name)),98 => @bitCast(@field(extra, field.name)),
9899
...@@ -838,7 +839,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -838,7 +839,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
838839
839 .if_simple,840 .if_simple,
840 .@"if",841 .@"if",
841 => return ifExpr(gz, scope, ri.br(), node, tree.fullIf(node).?),842 => {
843 const if_full = tree.fullIf(node).?;
844 if (if_full.error_token) |error_token| {
845 const tag = node_tags[if_full.ast.else_expr];
846 if ((tag == .@"switch" or tag == .switch_comma) and
847 std.mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(error_token + 4)))
848 {
849 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if");
850 }
851 }
852 return ifExpr(gz, scope, ri.br(), node, if_full);
853 },
842854
843 .while_simple,855 .while_simple,
844 .while_cont,856 .while_cont,
...@@ -1014,10 +1026,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -1014,10 +1026,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
1014 },1026 },
1015 .@"catch" => {1027 .@"catch" => {
1016 const catch_token = main_tokens[node];1028 const catch_token = main_tokens[node];
1017 const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)1029 const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) blk: {
1018 catch_token + 21030 if (token_tags.len > catch_token + 6 and
1019 else1031 token_tags[catch_token + 4] == .keyword_switch)
1020 null;1032 {
1033 if (std.mem.eql(u8, tree.tokenSlice(catch_token + 2), tree.tokenSlice(catch_token + 6))) {
1034 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch");
1035 }
1036 }
1037 break :blk catch_token + 2;
1038 } else null;
1021 switch (ri.rl) {1039 switch (ri.rl) {
1022 .ref, .ref_coerced_ty => return orelseCatchExpr(1040 .ref, .ref_coerced_ty => return orelseCatchExpr(
1023 gz,1041 gz,
...@@ -2556,7 +2574,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2556,7 +2574,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2556 .vector_type,2574 .vector_type,
2557 .indexable_ptr_len,2575 .indexable_ptr_len,
2558 .anyframe_type,2576 .anyframe_type,
2559 .as,
2560 .as_node,2577 .as_node,
2561 .as_shift_operand,2578 .as_shift_operand,
2562 .bit_and,2579 .bit_and,
...@@ -2641,6 +2658,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2641,6 +2658,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2641 .import,2658 .import,
2642 .switch_block,2659 .switch_block,
2643 .switch_block_ref,2660 .switch_block_ref,
2661 .switch_block_err_union,
2644 .union_init,2662 .union_init,
2645 .field_type_ref,2663 .field_type_ref,
2646 .error_set_decl,2664 .error_set_decl,
...@@ -6858,6 +6876,538 @@ fn forExpr(...@@ -6858,6 +6876,538 @@ fn forExpr(
6858 return result;6876 return result;
6859}6877}
68606878
6879fn switchExprErrUnion(
6880 parent_gz: *GenZir,
6881 scope: *Scope,
6882 ri: ResultInfo,
6883 catch_or_if_node: Ast.Node.Index,
6884 node_ty: enum { @"catch", @"if" },
6885) InnerError!Zir.Inst.Ref {
6886 const astgen = parent_gz.astgen;
6887 const gpa = astgen.gpa;
6888 const tree = astgen.tree;
6889 const node_datas = tree.nodes.items(.data);
6890 const node_tags = tree.nodes.items(.tag);
6891 const main_tokens = tree.nodes.items(.main_token);
6892 const token_tags = tree.tokens.items(.tag);
6893
6894 const if_full = switch (node_ty) {
6895 .@"catch" => undefined,
6896 .@"if" => tree.fullIf(catch_or_if_node).?,
6897 };
6898
6899 const switch_node, const operand_node, const error_payload = switch (node_ty) {
6900 .@"catch" => .{
6901 node_datas[catch_or_if_node].rhs,
6902 node_datas[catch_or_if_node].lhs,
6903 main_tokens[catch_or_if_node] + 2,
6904 },
6905 .@"if" => .{
6906 if_full.ast.else_expr,
6907 if_full.ast.cond_expr,
6908 if_full.error_token.?,
6909 },
6910 };
6911 assert(node_tags[switch_node] == .@"switch" or node_tags[switch_node] == .switch_comma);
6912
6913 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
6914 const case_nodes = tree.extra_data[extra.start..extra.end];
6915
6916 const need_rl = astgen.nodes_need_rl.contains(catch_or_if_node);
6917 const block_ri: ResultInfo = if (need_rl) ri else .{
6918 .rl = switch (ri.rl) {
6919 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_or_if_node)).? },
6920 .inferred_ptr => .none,
6921 else => ri.rl,
6922 },
6923 .ctx = ri.ctx,
6924 };
6925
6926 const payload_is_ref = node_ty == .@"if" and
6927 if_full.payload_token != null and token_tags[if_full.payload_token.?] == .asterisk;
6928
6929 // We need to call `rvalue` to write through to the pointer only if we had a
6930 // result pointer and aren't forwarding it.
6931 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
6932 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
6933 var scalar_cases_len: u32 = 0;
6934 var multi_cases_len: u32 = 0;
6935 var inline_cases_len: u32 = 0;
6936 var has_else = false;
6937 var else_node: Ast.Node.Index = 0;
6938 var else_src: ?Ast.TokenIndex = null;
6939 for (case_nodes) |case_node| {
6940 const case = tree.fullSwitchCase(case_node).?;
6941
6942 if (case.ast.values.len == 0) {
6943 const case_src = case.ast.arrow_token - 1;
6944 if (else_src) |src| {
6945 return astgen.failTokNotes(
6946 case_src,
6947 "multiple else prongs in switch expression",
6948 .{},
6949 &[_]u32{
6950 try astgen.errNoteTok(
6951 src,
6952 "previous else prong here",
6953 .{},
6954 ),
6955 },
6956 );
6957 }
6958 has_else = true;
6959 else_node = case_node;
6960 else_src = case_src;
6961 continue;
6962 } else if (case.ast.values.len == 1 and
6963 node_tags[case.ast.values[0]] == .identifier and
6964 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
6965 {
6966 const case_src = case.ast.arrow_token - 1;
6967 return astgen.failTokNotes(
6968 case_src,
6969 "'_' prong is not allowed when switching on errors",
6970 .{},
6971 &[_]u32{
6972 try astgen.errNoteTok(
6973 case_src,
6974 "consider using 'else'",
6975 .{},
6976 ),
6977 },
6978 );
6979 }
6980
6981 for (case.ast.values) |val| {
6982 if (node_tags[val] == .string_literal)
6983 return astgen.failNode(val, "cannot switch on strings", .{});
6984 }
6985
6986 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
6987 scalar_cases_len += 1;
6988 } else {
6989 multi_cases_len += 1;
6990 }
6991 if (case.inline_token != null) {
6992 inline_cases_len += 1;
6993 }
6994 }
6995
6996 const operand_ri: ResultInfo = .{
6997 .rl = if (payload_is_ref) .ref else .none,
6998 .ctx = .error_handling_expr,
6999 };
7000
7001 astgen.advanceSourceCursorToNode(operand_node);
7002 const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
7003
7004 const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node);
7005 const item_ri: ResultInfo = .{ .rl = .none };
7006
7007 // This contains the data that goes into the `extra` array for the SwitchBlockErrUnion, except
7008 // the first cases_nodes.len slots are a table that indexes payloads later in the array,
7009 // with the non-error and else case indices coming first, then scalar_cases_len indexes, then
7010 // multi_cases_len indexes
7011 const payloads = &astgen.scratch;
7012 const scratch_top = astgen.scratch.items.len;
7013 const case_table_start = scratch_top;
7014 const scalar_case_table = case_table_start + 1 + @intFromBool(has_else);
7015 const multi_case_table = scalar_case_table + scalar_cases_len;
7016 const case_table_end = multi_case_table + multi_cases_len;
7017
7018 try astgen.scratch.resize(gpa, case_table_end);
7019 defer astgen.scratch.items.len = scratch_top;
7020
7021 var block_scope = parent_gz.makeSubBlock(scope);
7022 // block_scope not used for collecting instructions
7023 block_scope.instructions_top = GenZir.unstacked_top;
7024 block_scope.setBreakResultInfo(block_ri);
7025
7026 // Sema expects a dbg_stmt immediately before switch_block_err_union
7027 try emitDbgStmt(parent_gz, operand_lc);
7028 // This gets added to the parent block later, after the item expressions.
7029 const switch_block = try parent_gz.makeBlockInst(.switch_block_err_union, switch_node);
7030
7031 // We re-use this same scope for all cases, including the special prong, if any.
7032 var case_scope = parent_gz.makeSubBlock(&block_scope.base);
7033 case_scope.instructions_top = GenZir.unstacked_top;
7034
7035 {
7036 const body_len_index: u32 = @intCast(payloads.items.len);
7037 payloads.items[case_table_start] = body_len_index;
7038 try payloads.resize(gpa, body_len_index + 1); // body_len
7039
7040 case_scope.instructions_top = parent_gz.instructions.items.len;
7041 defer case_scope.unstack();
7042
7043 try case_scope.addDbgBlockBegin();
7044
7045 const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref)
7046 .err_union_payload_unsafe_ptr
7047 else
7048 .err_union_payload_unsafe;
7049
7050 const unwrapped_payload = try case_scope.addUnNode(
7051 unwrap_payload_tag,
7052 raw_operand,
7053 catch_or_if_node,
7054 );
7055
7056 switch (node_ty) {
7057 .@"catch" => {
7058 const case_result = switch (ri.rl) {
7059 .ref, .ref_coerced_ty => unwrapped_payload,
7060 else => try rvalue(
7061 &case_scope,
7062 block_scope.break_result_info,
7063 unwrapped_payload,
7064 catch_or_if_node,
7065 ),
7066 };
7067 try case_scope.addDbgBlockEnd();
7068 _ = try case_scope.addBreakWithSrcNode(
7069 .@"break",
7070 switch_block,
7071 case_result,
7072 catch_or_if_node,
7073 );
7074 },
7075 .@"if" => {
7076 var payload_val_scope: Scope.LocalVal = undefined;
7077
7078 try case_scope.addDbgBlockBegin();
7079 const then_node = if_full.ast.then_expr;
7080 const then_sub_scope = s: {
7081 assert(if_full.error_token != null);
7082 if (if_full.payload_token) |payload_token| {
7083 const token_name_index = payload_token + @intFromBool(payload_is_ref);
7084 const ident_name = try astgen.identAsString(token_name_index);
7085 const token_name_str = tree.tokenSlice(token_name_index);
7086 if (mem.eql(u8, "_", token_name_str))
7087 break :s &case_scope.base;
7088 try astgen.detectLocalShadowing(
7089 &case_scope.base,
7090 ident_name,
7091 token_name_index,
7092 token_name_str,
7093 .capture,
7094 );
7095 payload_val_scope = .{
7096 .parent = &case_scope.base,
7097 .gen_zir = &case_scope,
7098 .name = ident_name,
7099 .inst = unwrapped_payload,
7100 .token_src = payload_token,
7101 .id_cat = .capture,
7102 };
7103 try case_scope.addDbgVar(.dbg_var_val, ident_name, unwrapped_payload);
7104 break :s &payload_val_scope.base;
7105 } else {
7106 _ = try case_scope.addUnNode(
7107 .ensure_err_union_payload_void,
7108 raw_operand,
7109 catch_or_if_node,
7110 );
7111 break :s &case_scope.base;
7112 }
7113 };
7114 const then_result = try expr(
7115 &case_scope,
7116 then_sub_scope,
7117 block_scope.break_result_info,
7118 then_node,
7119 );
7120 try checkUsed(parent_gz, &case_scope.base, then_sub_scope);
7121 if (!case_scope.endsWithNoReturn()) {
7122 try case_scope.addDbgBlockEnd();
7123 _ = try case_scope.addBreakWithSrcNode(
7124 .@"break",
7125 switch_block,
7126 then_result,
7127 then_node,
7128 );
7129 }
7130 },
7131 }
7132
7133 const case_slice = case_scope.instructionsSlice();
7134 // Since we use the switch_block_err_union instruction itself to refer
7135 // to the capture, which will not be added to the child block, we need
7136 // to handle ref_table manually.
7137 const refs_len = refs: {
7138 var n: usize = 0;
7139 var check_inst = switch_block;
7140 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7141 n += 1;
7142 check_inst = ref_inst;
7143 }
7144 break :refs n;
7145 };
7146 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
7147 try payloads.ensureUnusedCapacity(gpa, body_len);
7148 const capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = switch (node_ty) {
7149 .@"catch" => .none,
7150 .@"if" => if (if_full.payload_token == null)
7151 .none
7152 else if (payload_is_ref)
7153 .by_ref
7154 else
7155 .by_val,
7156 };
7157 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
7158 .body_len = @intCast(body_len),
7159 .capture = capture,
7160 .is_inline = false,
7161 .has_tag_capture = false,
7162 });
7163 if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
7164 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7165 }
7166 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
7167 }
7168
7169 const err_name = blk: {
7170 const err_str = tree.tokenSlice(error_payload);
7171 if (mem.eql(u8, err_str, "_")) {
7172 return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});
7173 }
7174 const err_name = try astgen.identAsString(error_payload);
7175 try astgen.detectLocalShadowing(scope, err_name, error_payload, err_str, .capture);
7176
7177 break :blk err_name;
7178 };
7179
7180 // allocate a shared dummy instruction for the error capture
7181 const err_inst = err_inst: {
7182 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
7183 try astgen.instructions.append(astgen.gpa, .{
7184 .tag = .extended,
7185 .data = .{ .extended = .{
7186 .opcode = .value_placeholder,
7187 .small = undefined,
7188 .operand = undefined,
7189 } },
7190 });
7191 break :err_inst inst;
7192 };
7193
7194 // In this pass we generate all the item and prong expressions for error cases.
7195 var multi_case_index: u32 = 0;
7196 var scalar_case_index: u32 = 0;
7197 var any_uses_err_capture = false;
7198 for (case_nodes) |case_node| {
7199 const case = tree.fullSwitchCase(case_node).?;
7200
7201 const is_multi_case = case.ast.values.len > 1 or
7202 (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
7203
7204 var dbg_var_name: Zir.NullTerminatedString = .empty;
7205 var dbg_var_inst: Zir.Inst.Ref = undefined;
7206 var err_scope: Scope.LocalVal = undefined;
7207 var capture_scope: Scope.LocalVal = undefined;
7208
7209 const sub_scope = blk: {
7210 err_scope = .{
7211 .parent = &case_scope.base,
7212 .gen_zir = &case_scope,
7213 .name = err_name,
7214 .inst = err_inst.toRef(),
7215 .token_src = error_payload,
7216 .id_cat = .capture,
7217 };
7218
7219 const capture_token = case.payload_token orelse break :blk &err_scope.base;
7220 assert(token_tags[capture_token] == .identifier);
7221
7222 const capture_slice = tree.tokenSlice(capture_token);
7223 if (mem.eql(u8, capture_slice, "_")) {
7224 return astgen.failTok(capture_token, "discard of error capture; omit it instead", .{});
7225 }
7226 const tag_name = try astgen.identAsString(capture_token);
7227 try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);
7228
7229 capture_scope = .{
7230 .parent = &case_scope.base,
7231 .gen_zir = &case_scope,
7232 .name = tag_name,
7233 .inst = switch_block.toRef(),
7234 .token_src = capture_token,
7235 .id_cat = .capture,
7236 };
7237 dbg_var_name = tag_name;
7238 dbg_var_inst = switch_block.toRef();
7239
7240 err_scope.parent = &capture_scope.base;
7241
7242 break :blk &err_scope.base;
7243 };
7244
7245 const header_index: u32 = @intCast(payloads.items.len);
7246 const body_len_index = if (is_multi_case) blk: {
7247 payloads.items[multi_case_table + multi_case_index] = header_index;
7248 multi_case_index += 1;
7249 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
7250
7251 // items
7252 var items_len: u32 = 0;
7253 for (case.ast.values) |item_node| {
7254 if (node_tags[item_node] == .switch_range) continue;
7255 items_len += 1;
7256
7257 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
7258 try payloads.append(gpa, @intFromEnum(item_inst));
7259 }
7260
7261 // ranges
7262 var ranges_len: u32 = 0;
7263 for (case.ast.values) |range| {
7264 if (node_tags[range] != .switch_range) continue;
7265 ranges_len += 1;
7266
7267 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs);
7268 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs);
7269 try payloads.appendSlice(gpa, &[_]u32{
7270 @intFromEnum(first), @intFromEnum(last),
7271 });
7272 }
7273
7274 payloads.items[header_index] = items_len;
7275 payloads.items[header_index + 1] = ranges_len;
7276 break :blk header_index + 2;
7277 } else if (case_node == else_node) blk: {
7278 payloads.items[case_table_start + 1] = header_index;
7279 try payloads.resize(gpa, header_index + 1); // body_len
7280 break :blk header_index;
7281 } else blk: {
7282 payloads.items[scalar_case_table + scalar_case_index] = header_index;
7283 scalar_case_index += 1;
7284 try payloads.resize(gpa, header_index + 2); // item, body_len
7285 const item_node = case.ast.values[0];
7286 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
7287 payloads.items[header_index] = @intFromEnum(item_inst);
7288 break :blk header_index + 1;
7289 };
7290
7291 {
7292 // temporarily stack case_scope on parent_gz
7293 case_scope.instructions_top = parent_gz.instructions.items.len;
7294 defer case_scope.unstack();
7295
7296 try case_scope.addDbgBlockBegin();
7297 if (dbg_var_name != .empty) {
7298 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
7299 }
7300 const target_expr_node = case.ast.target_expr;
7301 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
7302 // check capture_scope, not err_scope to avoid false positive unused error capture
7303 try checkUsed(parent_gz, &case_scope.base, err_scope.parent);
7304 const uses_err = err_scope.used != 0 or err_scope.discarded != 0;
7305 if (uses_err) {
7306 try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef());
7307 any_uses_err_capture = true;
7308 }
7309 try case_scope.addDbgBlockEnd();
7310 if (!parent_gz.refIsNoReturn(case_result)) {
7311 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
7312 }
7313
7314 const case_slice = case_scope.instructionsSlice();
7315 // Since we use the switch_block_err_union instruction itself to refer
7316 // to the capture, which will not be added to the child block, we need
7317 // to handle ref_table manually.
7318 const refs_len = refs: {
7319 var n: usize = 0;
7320 var check_inst = switch_block;
7321 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7322 n += 1;
7323 check_inst = ref_inst;
7324 }
7325 if (uses_err) {
7326 check_inst = err_inst;
7327 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7328 n += 1;
7329 check_inst = ref_inst;
7330 }
7331 }
7332 break :refs n;
7333 };
7334 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
7335 try payloads.ensureUnusedCapacity(gpa, body_len);
7336 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
7337 .body_len = @intCast(body_len),
7338 .capture = if (case.payload_token != null) .by_val else .none,
7339 .is_inline = case.inline_token != null,
7340 .has_tag_capture = false,
7341 });
7342 if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
7343 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7344 }
7345 if (uses_err) {
7346 if (astgen.ref_table.fetchRemove(err_inst)) |kv| {
7347 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7348 }
7349 }
7350 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
7351 }
7352 }
7353 // Now that the item expressions are generated we can add this.
7354 try parent_gz.instructions.append(gpa, switch_block);
7355
7356 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlockErrUnion).Struct.fields.len +
7357 @intFromBool(multi_cases_len != 0) +
7358 payloads.items.len - case_table_end +
7359 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).Struct.fields.len);
7360
7361 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlockErrUnion{
7362 .operand = raw_operand,
7363 .bits = Zir.Inst.SwitchBlockErrUnion.Bits{
7364 .has_multi_cases = multi_cases_len != 0,
7365 .has_else = has_else,
7366 .scalar_cases_len = @intCast(scalar_cases_len),
7367 .any_uses_err_capture = any_uses_err_capture,
7368 .payload_is_ref = payload_is_ref,
7369 },
7370 .main_src_node_offset = parent_gz.nodeIndexToRelative(catch_or_if_node),
7371 });
7372
7373 if (multi_cases_len != 0) {
7374 astgen.extra.appendAssumeCapacity(multi_cases_len);
7375 }
7376
7377 if (any_uses_err_capture) {
7378 astgen.extra.appendAssumeCapacity(@intFromEnum(err_inst));
7379 }
7380
7381 const zir_datas = astgen.instructions.items(.data);
7382 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
7383
7384 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
7385 var body_len_index = start_index;
7386 var end_index = start_index;
7387 const table_index = case_table_start + i;
7388 if (table_index < scalar_case_table) {
7389 end_index += 1;
7390 } else if (table_index < multi_case_table) {
7391 body_len_index += 1;
7392 end_index += 2;
7393 } else {
7394 body_len_index += 2;
7395 const items_len = payloads.items[start_index];
7396 const ranges_len = payloads.items[start_index + 1];
7397 end_index += 3 + items_len + 2 * ranges_len;
7398 }
7399 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
7400 end_index += prong_info.body_len;
7401 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
7402 }
7403
7404 if (need_result_rvalue) {
7405 return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
7406 } else {
7407 return switch_block.toRef();
7408 }
7409}
7410
6861fn switchExpr(7411fn switchExpr(
6862 parent_gz: *GenZir,7412 parent_gz: *GenZir,
6863 scope: *Scope,7413 scope: *Scope,
src/Sema.zig+718-287
...@@ -1018,7 +1018,6 @@ fn analyzeBodyInner(...@@ -1018,7 +1018,6 @@ fn analyzeBodyInner(
1018 .array_type => try sema.zirArrayType(block, inst),1018 .array_type => try sema.zirArrayType(block, inst),
1019 .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst),1019 .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst),
1020 .vector_type => try sema.zirVectorType(block, inst),1020 .vector_type => try sema.zirVectorType(block, inst),
1021 .as => try sema.zirAs(block, inst),
1022 .as_node => try sema.zirAsNode(block, inst),1021 .as_node => try sema.zirAsNode(block, inst),
1023 .as_shift_operand => try sema.zirAsShiftOperand(block, inst),1022 .as_shift_operand => try sema.zirAsShiftOperand(block, inst),
1024 .bit_and => try sema.zirBitwise(block, inst, .bit_and),1023 .bit_and => try sema.zirBitwise(block, inst, .bit_and),
...@@ -1098,6 +1097,7 @@ fn analyzeBodyInner(...@@ -1098,6 +1097,7 @@ fn analyzeBodyInner(
1098 .str => try sema.zirStr(inst),1097 .str => try sema.zirStr(inst),
1099 .switch_block => try sema.zirSwitchBlock(block, inst, false),1098 .switch_block => try sema.zirSwitchBlock(block, inst, false),
1100 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true),1099 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true),
1100 .switch_block_err_union => try sema.zirSwitchBlockErrUnion(block, inst),
1101 .type_info => try sema.zirTypeInfo(block, inst),1101 .type_info => try sema.zirTypeInfo(block, inst),
1102 .size_of => try sema.zirSizeOf(block, inst),1102 .size_of => try sema.zirSizeOf(block, inst),
1103 .bit_size_of => try sema.zirBitSizeOf(block, inst),1103 .bit_size_of => try sema.zirBitSizeOf(block, inst),
...@@ -8939,10 +8939,14 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -8939,10 +8939,14 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
8939 const tracy = trace(@src());8939 const tracy = trace(@src());
8940 defer tracy.end();8940 defer tracy.end();
89418941
8942 const mod = sema.mod;
8943 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;8942 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
8944 const src = inst_data.src();8943 const src = inst_data.src();
8945 const operand = try sema.resolveInst(inst_data.operand);8944 const operand = try sema.resolveInst(inst_data.operand);
8945 return sema.analyzeErrUnionCodePtr(block, src, operand);
8946}
8947
8948fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref {
8949 const mod = sema.mod;
8946 const operand_ty = sema.typeOf(operand);8950 const operand_ty = sema.typeOf(operand);
8947 assert(operand_ty.zigTypeTag(mod) == .Pointer);8951 assert(operand_ty.zigTypeTag(mod) == .Pointer);
89488952
...@@ -8957,7 +8961,10 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -8957,7 +8961,10 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
8957 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {8961 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
8958 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {8962 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {
8959 assert(val.getErrorName(mod) != .none);8963 assert(val.getErrorName(mod) != .none);
8960 return Air.internedToRef(val.toIntern());8964 return Air.internedToRef((try mod.intern(.{ .err = .{
8965 .ty = result_ty.toIntern(),
8966 .name = mod.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
8967 } })));
8961 }8968 }
8962 }8969 }
89638970
...@@ -9794,14 +9801,6 @@ fn zirParamAnytype(...@@ -9794,14 +9801,6 @@ fn zirParamAnytype(
9794 sema.inst_map.putAssumeCapacity(inst, .generic_poison);9801 sema.inst_map.putAssumeCapacity(inst, .generic_poison);
9795}9802}
97969803
9797fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9798 const tracy = trace(@src());
9799 defer tracy.end();
9800
9801 const bin_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
9802 return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs, false);
9803}
9804
9805fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9804fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9806 const tracy = trace(@src());9805 const tracy = trace(@src());
9807 defer tracy.end();9806 defer tracy.end();
...@@ -11168,13 +11167,312 @@ fn switchCond(...@@ -11168,13 +11167,312 @@ fn switchCond(
1116811167
11169const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, Module.SwitchProngSrc);11168const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, Module.SwitchProngSrc);
1117011169
11170fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11171 const tracy = trace(@src());
11172 defer tracy.end();
11173
11174 const mod = sema.mod;
11175 const gpa = sema.gpa;
11176 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
11177 const switch_src = inst_data.src();
11178 const switch_src_node_offset = inst_data.src_node;
11179 const switch_operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_src_node_offset };
11180 const else_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = switch_src_node_offset };
11181 const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);
11182 const main_operand_src: LazySrcLoc = .{ .node_offset_if_cond = extra.data.main_src_node_offset };
11183 const main_src: LazySrcLoc = .{ .node_offset_main_token = extra.data.main_src_node_offset };
11184
11185 const raw_operand_val = try sema.resolveInst(extra.data.operand);
11186
11187 // AstGen guarantees that the instruction immediately preceding
11188 // switch_block_err_union is a dbg_stmt
11189 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
11190
11191 var header_extra_index: usize = extra.end;
11192
11193 const scalar_cases_len = extra.data.bits.scalar_cases_len;
11194 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
11195 const multi_cases_len = sema.code.extra[header_extra_index];
11196 header_extra_index += 1;
11197 break :blk multi_cases_len;
11198 } else 0;
11199
11200 const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: {
11201 const err_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
11202 header_extra_index += 1;
11203 // SwitchProngAnalysis wants inst_map to have space for the tag capture.
11204 // Note that the normal capture is referred to via the switch block
11205 // index, which there is already necessarily space for.
11206 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{err_capture_inst});
11207 break :blk err_capture_inst;
11208 } else undefined;
11209
11210 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
11211 defer case_vals.deinit(gpa);
11212
11213 const NonError = struct {
11214 body: []const Zir.Inst.Index,
11215 end: usize,
11216 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
11217 };
11218
11219 const non_error_case: NonError = non_error: {
11220 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
11221 const extra_body_start = header_extra_index + 1;
11222 break :non_error .{
11223 .body = sema.code.bodySlice(extra_body_start, info.body_len),
11224 .end = extra_body_start + info.body_len,
11225 .capture = info.capture,
11226 };
11227 };
11228
11229 const Else = struct {
11230 body: []const Zir.Inst.Index,
11231 end: usize,
11232 is_inline: bool,
11233 has_capture: bool,
11234 };
11235
11236 const else_case: Else = if (!extra.data.bits.has_else) .{
11237 .body = &.{},
11238 .end = non_error_case.end,
11239 .is_inline = false,
11240 .has_capture = false,
11241 } else special: {
11242 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[non_error_case.end]);
11243 const extra_body_start = non_error_case.end + 1;
11244 assert(info.capture != .by_ref);
11245 assert(!info.has_tag_capture);
11246 break :special .{
11247 .body = sema.code.bodySlice(extra_body_start, info.body_len),
11248 .end = extra_body_start + info.body_len,
11249 .is_inline = info.is_inline,
11250 .has_capture = info.capture != .none,
11251 };
11252 };
11253
11254 var seen_errors = SwitchErrorSet.init(gpa);
11255 defer seen_errors.deinit();
11256
11257 const operand_ty = sema.typeOf(raw_operand_val);
11258 const operand_err_set_ty = if (extra.data.bits.payload_is_ref)
11259 operand_ty.childType(mod).errorUnionSet(mod)
11260 else
11261 operand_ty.errorUnionSet(mod);
11262
11263 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
11264 try sema.air_instructions.append(gpa, .{
11265 .tag = .block,
11266 .data = undefined,
11267 });
11268 var label: Block.Label = .{
11269 .zir_block = inst,
11270 .merges = .{
11271 .src_locs = .{},
11272 .results = .{},
11273 .br_list = .{},
11274 .block_inst = block_inst,
11275 },
11276 };
11277
11278 var child_block: Block = .{
11279 .parent = block,
11280 .sema = sema,
11281 .src_decl = block.src_decl,
11282 .namespace = block.namespace,
11283 .wip_capture_scope = block.wip_capture_scope,
11284 .instructions = .{},
11285 .label = &label,
11286 .inlining = block.inlining,
11287 .is_comptime = block.is_comptime,
11288 .comptime_reason = block.comptime_reason,
11289 .is_typeof = block.is_typeof,
11290 .c_import_buf = block.c_import_buf,
11291 .runtime_cond = block.runtime_cond,
11292 .runtime_loop = block.runtime_loop,
11293 .runtime_index = block.runtime_index,
11294 .error_return_trace_index = block.error_return_trace_index,
11295 .want_safety = block.want_safety,
11296 };
11297 const merges = &child_block.label.?.merges;
11298 defer child_block.instructions.deinit(gpa);
11299 defer merges.deinit(gpa);
11300
11301 const resolved_err_set = try sema.resolveInferredErrorSetTy(block, main_src, operand_err_set_ty.toIntern());
11302 if (Type.fromInterned(resolved_err_set).errorSetIsEmpty(mod)) {
11303 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
11304 }
11305
11306 const else_error_ty: ?Type = try validateErrSetSwitch(
11307 sema,
11308 block,
11309 &seen_errors,
11310 &case_vals,
11311 operand_err_set_ty,
11312 inst_data,
11313 scalar_cases_len,
11314 multi_cases_len,
11315 .{ .body = else_case.body, .end = else_case.end, .src = else_prong_src },
11316 extra.data.bits.has_else,
11317 );
11318
11319 var spa: SwitchProngAnalysis = .{
11320 .sema = sema,
11321 .parent_block = block,
11322 .operand = undefined, // must be set to the unwrapped error code before use
11323 .operand_ptr = .none,
11324 .cond = raw_operand_val,
11325 .else_error_ty = else_error_ty,
11326 .switch_block_inst = inst,
11327 .tag_capture_inst = undefined,
11328 };
11329
11330 if (try sema.resolveDefinedValue(&child_block, main_src, raw_operand_val)) |ov| {
11331 const operand_val = if (extra.data.bits.payload_is_ref)
11332 (try sema.pointerDeref(&child_block, main_src, ov, operand_ty)).?
11333 else
11334 ov;
11335
11336 if (operand_val.errorUnionIsPayload(mod)) {
11337 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
11338 } else {
11339 const err_val = Value.fromInterned(try mod.intern(.{
11340 .err = .{
11341 .ty = operand_err_set_ty.toIntern(),
11342 .name = operand_val.getErrorName(mod).unwrap().?,
11343 },
11344 }));
11345 spa.operand = if (extra.data.bits.payload_is_ref)
11346 try sema.analyzeErrUnionCodePtr(block, switch_operand_src, raw_operand_val)
11347 else
11348 try sema.analyzeErrUnionCode(block, switch_operand_src, raw_operand_val);
11349
11350 if (extra.data.bits.any_uses_err_capture) {
11351 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand);
11352 }
11353 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
11354
11355 return resolveSwitchComptime(
11356 sema,
11357 spa,
11358 &child_block,
11359 try sema.switchCond(block, switch_operand_src, spa.operand),
11360 err_val,
11361 operand_err_set_ty,
11362 .{
11363 .body = else_case.body,
11364 .end = else_case.end,
11365 .capture = if (else_case.has_capture) .by_val else .none,
11366 .is_inline = else_case.is_inline,
11367 .has_tag_capture = false,
11368 },
11369 case_vals,
11370 scalar_cases_len,
11371 multi_cases_len,
11372 true,
11373 false,
11374 );
11375 }
11376 }
11377
11378 if (scalar_cases_len + multi_cases_len == 0) {
11379 if (else_error_ty) |ty| if (ty.errorSetIsEmpty(mod)) {
11380 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
11381 };
11382 }
11383
11384 if (child_block.is_comptime) {
11385 _ = try sema.resolveConstDefinedValue(&child_block, main_operand_src, raw_operand_val, .{
11386 .needed_comptime_reason = "condition in comptime switch must be comptime-known",
11387 .block_comptime_reason = child_block.comptime_reason,
11388 });
11389 unreachable;
11390 }
11391
11392 const cond = if (extra.data.bits.payload_is_ref) blk: {
11393 try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val).elemType2(mod));
11394 const loaded = try sema.analyzeLoad(block, main_src, raw_operand_val, main_src);
11395 break :blk try sema.analyzeIsNonErr(block, main_src, loaded);
11396 } else blk: {
11397 try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val));
11398 break :blk try sema.analyzeIsNonErr(block, main_src, raw_operand_val);
11399 };
11400
11401 var sub_block = child_block.makeSubBlock();
11402 sub_block.runtime_loop = null;
11403 sub_block.runtime_cond = main_operand_src;
11404 sub_block.runtime_index.increment();
11405 defer sub_block.instructions.deinit(gpa);
11406
11407 try sema.analyzeBodyRuntimeBreak(&sub_block, non_error_case.body);
11408 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
11409 defer gpa.free(true_instructions);
11410
11411 spa.operand = if (extra.data.bits.payload_is_ref)
11412 try sema.analyzeErrUnionCodePtr(&sub_block, switch_operand_src, raw_operand_val)
11413 else
11414 try sema.analyzeErrUnionCode(&sub_block, switch_operand_src, raw_operand_val);
11415
11416 if (extra.data.bits.any_uses_err_capture) {
11417 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand);
11418 }
11419 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
11420 _ = try sema.analyzeSwitchRuntimeBlock(
11421 spa,
11422 &sub_block,
11423 switch_src,
11424 try sema.switchCond(block, switch_operand_src, spa.operand),
11425 operand_err_set_ty,
11426 switch_operand_src,
11427 case_vals,
11428 .{
11429 .body = else_case.body,
11430 .end = else_case.end,
11431 .capture = if (else_case.has_capture) .by_val else .none,
11432 .is_inline = else_case.is_inline,
11433 .has_tag_capture = false,
11434 },
11435 scalar_cases_len,
11436 multi_cases_len,
11437 false,
11438 undefined,
11439 true,
11440 switch_src_node_offset,
11441 else_prong_src,
11442 undefined,
11443 seen_errors,
11444 undefined,
11445 undefined,
11446 undefined,
11447 cond_dbg_node_index,
11448 true,
11449 );
11450
11451 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
11452 true_instructions.len + sub_block.instructions.items.len);
11453
11454 _ = try child_block.addInst(.{
11455 .tag = .cond_br,
11456 .data = .{ .pl_op = .{
11457 .operand = cond,
11458 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
11459 .then_body_len = @intCast(true_instructions.len),
11460 .else_body_len = @intCast(sub_block.instructions.items.len),
11461 }),
11462 } },
11463 });
11464 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
11465 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
11466
11467 return sema.analyzeBlockBody(block, main_src, &child_block, merges);
11468}
11469
11171fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref {11470fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref {
11172 const tracy = trace(@src());11471 const tracy = trace(@src());
11173 defer tracy.end();11472 defer tracy.end();
1117411473
11175 const mod = sema.mod;11474 const mod = sema.mod;
11176 const gpa = sema.gpa;11475 const gpa = sema.gpa;
11177 const ip = &mod.intern_pool;
11178 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;11476 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
11179 const src = inst_data.src();11477 const src = inst_data.src();
11180 const src_node_offset = inst_data.src_node;11478 const src_node_offset = inst_data.src_node;
...@@ -11220,16 +11518,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11220,16 +11518,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11220 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);11518 var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
11221 defer case_vals.deinit(gpa);11519 defer case_vals.deinit(gpa);
1122211520
11223 const Special = struct {
11224 body: []const Zir.Inst.Index,
11225 end: usize,
11226 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
11227 is_inline: bool,
11228 has_tag_capture: bool,
11229 };
11230
11231 const special_prong = extra.data.bits.specialProng();11521 const special_prong = extra.data.bits.specialProng();
11232 const special: Special = switch (special_prong) {11522 const special: SpecialProng = switch (special_prong) {
11233 .none => .{11523 .none => .{
11234 .body = &.{},11524 .body = &.{},
11235 .end = header_extra_index,11525 .end = header_extra_index,
...@@ -11409,150 +11699,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11409,150 +11699,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11409 );11699 );
11410 }11700 }
11411 },11701 },
11412 .ErrorSet => {11702 .ErrorSet => else_error_ty = try validateErrSetSwitch(
11413 var extra_index: usize = special.end;11703 sema,
11414 {11704 block,
11415 var scalar_i: u32 = 0;11705 &seen_errors,
11416 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {11706 &case_vals,
11417 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);11707 operand_ty,
11418 extra_index += 1;11708 inst_data,
11419 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);11709 scalar_cases_len,
11420 extra_index += 1 + info.body_len;11710 multi_cases_len,
1142111711 .{ .body = special.body, .end = special.end, .src = special_prong_src },
11422 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(11712 special_prong == .@"else",
11423 block,11713 ),
11424 &seen_errors,
11425 item_ref,
11426 operand_ty,
11427 src_node_offset,
11428 .{ .scalar = scalar_i },
11429 ));
11430 }
11431 }
11432 {
11433 var multi_i: u32 = 0;
11434 while (multi_i < multi_cases_len) : (multi_i += 1) {
11435 const items_len = sema.code.extra[extra_index];
11436 extra_index += 1;
11437 const ranges_len = sema.code.extra[extra_index];
11438 extra_index += 1;
11439 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11440 extra_index += 1;
11441 const items = sema.code.refSlice(extra_index, items_len);
11442 extra_index += items_len + info.body_len;
11443
11444 try case_vals.ensureUnusedCapacity(gpa, items.len);
11445 for (items, 0..) |item_ref, item_i| {
11446 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
11447 block,
11448 &seen_errors,
11449 item_ref,
11450 operand_ty,
11451 src_node_offset,
11452 .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } },
11453 ));
11454 }
11455
11456 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
11457 }
11458 }
11459
11460 switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) {
11461 .anyerror_type => {
11462 if (special_prong != .@"else") {
11463 return sema.fail(
11464 block,
11465 src,
11466 "else prong required when switching on type 'anyerror'",
11467 .{},
11468 );
11469 }
11470 else_error_ty = Type.anyerror;
11471 },
11472 else => |err_set_ty_index| else_validation: {
11473 const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names;
11474 var maybe_msg: ?*Module.ErrorMsg = null;
11475 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
11476
11477 for (error_names.get(ip)) |error_name| {
11478 if (!seen_errors.contains(error_name) and special_prong != .@"else") {
11479 const msg = maybe_msg orelse blk: {
11480 maybe_msg = try sema.errMsg(
11481 block,
11482 src,
11483 "switch must handle all possibilities",
11484 .{},
11485 );
11486 break :blk maybe_msg.?;
11487 };
11488
11489 try sema.errNote(
11490 block,
11491 src,
11492 msg,
11493 "unhandled error value: 'error.{}'",
11494 .{error_name.fmt(ip)},
11495 );
11496 }
11497 }
11498
11499 if (maybe_msg) |msg| {
11500 maybe_msg = null;
11501 try sema.addDeclaredHereNote(msg, operand_ty);
11502 return sema.failWithOwnedErrorMsg(block, msg);
11503 }
11504
11505 if (special_prong == .@"else" and
11506 seen_errors.count() == error_names.len)
11507 {
11508 // In order to enable common patterns for generic code allow simple else bodies
11509 // else => unreachable,
11510 // else => return,
11511 // else => |e| return e,
11512 // even if all the possible errors were already handled.
11513 const tags = sema.code.instructions.items(.tag);
11514 for (special.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) {
11515 .dbg_block_begin,
11516 .dbg_block_end,
11517 .dbg_stmt,
11518 .dbg_var_val,
11519 .ret_type,
11520 .as_node,
11521 .ret_node,
11522 .@"unreachable",
11523 .@"defer",
11524 .defer_err_code,
11525 .err_union_code,
11526 .ret_err_value_code,
11527 .restore_err_ret_index,
11528 .is_non_err,
11529 .ret_is_non_err,
11530 .condbr,
11531 => {},
11532 else => break,
11533 } else break :else_validation;
11534
11535 return sema.fail(
11536 block,
11537 special_prong_src,
11538 "unreachable else prong; all cases already handled",
11539 .{},
11540 );
11541 }
11542
11543 var names: InferredErrorSet.NameMap = .{};
11544 try names.ensureUnusedCapacity(sema.arena, error_names.len);
11545 for (error_names.get(ip)) |error_name| {
11546 if (seen_errors.contains(error_name)) continue;
11547
11548 names.putAssumeCapacityNoClobber(error_name, {});
11549 }
11550 // No need to keep the hash map metadata correct; here we
11551 // extract the (sorted) keys only.
11552 else_error_ty = try mod.errorSetFromUnsortedNames(names.keys());
11553 },
11554 }
11555 },
11556 .Int, .ComptimeInt => {11714 .Int, .ComptimeInt => {
11557 var extra_index: usize = special.end;11715 var extra_index: usize = special.end;
11558 {11716 {
...@@ -11848,114 +12006,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11848,114 +12006,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11848 defer merges.deinit(gpa);12006 defer merges.deinit(gpa);
1184912007
11850 if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| {12008 if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| {
11851 const resolved_operand_val = try sema.resolveLazyValue(operand_val);12009 return resolveSwitchComptime(
11852 var extra_index: usize = special.end;12010 sema,
11853 {12011 spa,
11854 var scalar_i: usize = 0;
11855 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
11856 extra_index += 1;
11857 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11858 extra_index += 1;
11859 const body = sema.code.bodySlice(extra_index, info.body_len);
11860 extra_index += info.body_len;
11861
11862 const item = case_vals.items[scalar_i];
11863 const item_val = sema.resolveConstDefinedValue(&child_block, .unneeded, item, undefined) catch unreachable;
11864 if (operand_val.eql(item_val, operand_ty, sema.mod)) {
11865 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
11866 return spa.resolveProngComptime(
11867 &child_block,
11868 .normal,
11869 body,
11870 info.capture,
11871 .{ .scalar_capture = @intCast(scalar_i) },
11872 &.{item},
11873 if (info.is_inline) operand else .none,
11874 info.has_tag_capture,
11875 merges,
11876 );
11877 }
11878 }
11879 }
11880 {
11881 var multi_i: usize = 0;
11882 var case_val_idx: usize = scalar_cases_len;
11883 while (multi_i < multi_cases_len) : (multi_i += 1) {
11884 const items_len = sema.code.extra[extra_index];
11885 extra_index += 1;
11886 const ranges_len = sema.code.extra[extra_index];
11887 extra_index += 1;
11888 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11889 extra_index += 1 + items_len;
11890 const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len);
11891
11892 const items = case_vals.items[case_val_idx..][0..items_len];
11893 case_val_idx += items_len;
11894
11895 for (items) |item| {
11896 // Validation above ensured these will succeed.
11897 const item_val = sema.resolveConstDefinedValue(&child_block, .unneeded, item, undefined) catch unreachable;
11898 if (operand_val.eql(item_val, operand_ty, sema.mod)) {
11899 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
11900 return spa.resolveProngComptime(
11901 &child_block,
11902 .normal,
11903 body,
11904 info.capture,
11905 .{ .multi_capture = @intCast(multi_i) },
11906 items,
11907 if (info.is_inline) operand else .none,
11908 info.has_tag_capture,
11909 merges,
11910 );
11911 }
11912 }
11913
11914 var range_i: usize = 0;
11915 while (range_i < ranges_len) : (range_i += 1) {
11916 const range_items = case_vals.items[case_val_idx..][0..2];
11917 extra_index += 2;
11918 case_val_idx += 2;
11919
11920 // Validation above ensured these will succeed.
11921 const first_val = sema.resolveConstDefinedValue(&child_block, .unneeded, range_items[0], undefined) catch unreachable;
11922 const last_val = sema.resolveConstDefinedValue(&child_block, .unneeded, range_items[1], undefined) catch unreachable;
11923 if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and
11924 (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty)))
11925 {
11926 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
11927 return spa.resolveProngComptime(
11928 &child_block,
11929 .normal,
11930 body,
11931 info.capture,
11932 .{ .multi_capture = @intCast(multi_i) },
11933 undefined, // case_vals may be undefined for ranges
11934 if (info.is_inline) operand else .none,
11935 info.has_tag_capture,
11936 merges,
11937 );
11938 }
11939 }
11940
11941 extra_index += info.body_len;
11942 }
11943 }
11944 if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, special.body, operand);
11945 if (empty_enum) {
11946 return .void_value;
11947 }
11948
11949 return spa.resolveProngComptime(
11950 &child_block,12012 &child_block,
11951 .special,12013 operand,
11952 special.body,12014 operand_val,
11953 special.capture,12015 operand_ty,
11954 .special_capture,12016 special,
11955 undefined, // case_vals may be undefined for special prongs12017 case_vals,
11956 if (special.is_inline) operand else .none,12018 scalar_cases_len,
11957 special.has_tag_capture,12019 multi_cases_len,
11958 merges,12020 err_set,
12021 empty_enum,
11959 );12022 );
11960 }12023 }
1196112024
...@@ -11966,7 +12029,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11966,7 +12029,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11966 if (special_prong == .none) {12029 if (special_prong == .none) {
11967 return sema.fail(block, src, "switch must handle all possibilities", .{});12030 return sema.fail(block, src, "switch must handle all possibilities", .{});
11968 }12031 }
11969 if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand, operand_src)) {12032 if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand, operand_src, false)) {
11970 return .unreachable_value;12033 return .unreachable_value;
11971 }12034 }
11972 if (mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and12035 if (mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and
...@@ -11998,6 +12061,73 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11998,6 +12061,73 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
11998 unreachable;12061 unreachable;
11999 }12062 }
1200012063
12064 _ = try sema.analyzeSwitchRuntimeBlock(
12065 spa,
12066 &child_block,
12067 src,
12068 operand,
12069 operand_ty,
12070 operand_src,
12071 case_vals,
12072 special,
12073 scalar_cases_len,
12074 multi_cases_len,
12075 union_originally,
12076 maybe_union_ty,
12077 err_set,
12078 src_node_offset,
12079 special_prong_src,
12080 seen_enum_fields,
12081 seen_errors,
12082 range_set,
12083 true_count,
12084 false_count,
12085 cond_dbg_node_index,
12086 false,
12087 );
12088
12089 return sema.analyzeBlockBody(block, src, &child_block, merges);
12090}
12091
12092const SpecialProng = struct {
12093 body: []const Zir.Inst.Index,
12094 end: usize,
12095 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
12096 is_inline: bool,
12097 has_tag_capture: bool,
12098};
12099
12100fn analyzeSwitchRuntimeBlock(
12101 sema: *Sema,
12102 spa: SwitchProngAnalysis,
12103 child_block: *Block,
12104 src: LazySrcLoc,
12105 operand: Air.Inst.Ref,
12106 operand_ty: Type,
12107 operand_src: LazySrcLoc,
12108 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
12109 special: SpecialProng,
12110 scalar_cases_len: usize,
12111 multi_cases_len: usize,
12112 union_originally: bool,
12113 maybe_union_ty: Type,
12114 err_set: bool,
12115 src_node_offset: i32,
12116 special_prong_src: LazySrcLoc,
12117 seen_enum_fields: []?Module.SwitchProngSrc,
12118 seen_errors: SwitchErrorSet,
12119 range_set: RangeSet,
12120 true_count: u8,
12121 false_count: u8,
12122 cond_dbg_node_index: Zir.Inst.Index,
12123 allow_err_code_unwrap: bool,
12124) CompileError!Air.Inst.Ref {
12125 const mod = sema.mod;
12126 const gpa = sema.gpa;
12127 const ip = &mod.intern_pool;
12128
12129 const block = child_block.parent.?;
12130
12001 const estimated_cases_extra = (scalar_cases_len + multi_cases_len) *12131 const estimated_cases_extra = (scalar_cases_len + multi_cases_len) *
12002 @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2;12132 @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2;
12003 var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra);12133 var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra);
...@@ -12032,7 +12162,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -12032,7 +12162,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
12032 break :blk field_ty.zigTypeTag(mod) != .NoReturn;12162 break :blk field_ty.zigTypeTag(mod) != .NoReturn;
12033 } else true;12163 } else true;
1203412164
12035 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) {12165 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) {
12036 // nothing to do here12166 // nothing to do here
12037 } else if (analyze_body) {12167 } else if (analyze_body) {
12038 try spa.analyzeProngRuntime(12168 try spa.analyzeProngRuntime(
...@@ -12216,7 +12346,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -12216,7 +12346,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1221612346
12217 const body = sema.code.bodySlice(extra_index, info.body_len);12347 const body = sema.code.bodySlice(extra_index, info.body_len);
12218 extra_index += info.body_len;12348 extra_index += info.body_len;
12219 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) {12349 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) {
12220 // nothing to do here12350 // nothing to do here
12221 } else if (analyze_body) {12351 } else if (analyze_body) {
12222 try spa.analyzeProngRuntime(12352 try spa.analyzeProngRuntime(
...@@ -12300,7 +12430,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -12300,7 +12430,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1230012430
12301 const body = sema.code.bodySlice(extra_index, info.body_len);12431 const body = sema.code.bodySlice(extra_index, info.body_len);
12302 extra_index += info.body_len;12432 extra_index += info.body_len;
12303 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) {12433 if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) {
12304 // nothing to do here12434 // nothing to do here
12305 } else {12435 } else {
12306 try spa.analyzeProngRuntime(12436 try spa.analyzeProngRuntime(
...@@ -12543,7 +12673,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -12543,7 +12673,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
12543 else12673 else
12544 true;12674 true;
12545 if (special.body.len != 0 and err_set and12675 if (special.body.len != 0 and err_set and
12546 try sema.maybeErrorUnwrap(&case_block, special.body, operand, operand_src))12676 try sema.maybeErrorUnwrap(&case_block, special.body, operand, operand_src, allow_err_code_unwrap))
12547 {12677 {
12548 // nothing to do here12678 // nothing to do here
12549 } else if (special.body.len != 0 and analyze_body and !special.is_inline) {12679 } else if (special.body.len != 0 and analyze_body and !special.is_inline) {
...@@ -12588,17 +12718,147 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -12588,17 +12718,147 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
12588 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len +12718 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len +
12589 cases_extra.items.len + final_else_body.len);12719 cases_extra.items.len + final_else_body.len);
1259012720
12591 _ = try child_block.addInst(.{ .tag = .switch_br, .data = .{ .pl_op = .{12721 const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{
12592 .operand = operand,12722 .cases_len = @intCast(cases_len),
12593 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{12723 .else_body_len = @intCast(final_else_body.len),
12594 .cases_len = @intCast(cases_len),12724 });
12595 .else_body_len = @intCast(final_else_body.len),12725
12596 }),
12597 } } });
12598 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items));12726 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items));
12599 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(final_else_body));12727 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(final_else_body));
1260012728
12601 return sema.analyzeBlockBody(block, src, &child_block, merges);12729 return try child_block.addInst(.{
12730 .tag = .switch_br,
12731 .data = .{ .pl_op = .{
12732 .operand = operand,
12733 .payload = payload_index,
12734 } },
12735 });
12736}
12737
12738fn resolveSwitchComptime(
12739 sema: *Sema,
12740 spa: SwitchProngAnalysis,
12741 child_block: *Block,
12742 cond_operand: Air.Inst.Ref,
12743 operand_val: Value,
12744 operand_ty: Type,
12745 special: SpecialProng,
12746 case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
12747 scalar_cases_len: u32,
12748 multi_cases_len: u32,
12749 err_set: bool,
12750 empty_enum: bool,
12751) CompileError!Air.Inst.Ref {
12752 const merges = &child_block.label.?.merges;
12753 const resolved_operand_val = try sema.resolveLazyValue(operand_val);
12754 var extra_index: usize = special.end;
12755 {
12756 var scalar_i: usize = 0;
12757 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
12758 extra_index += 1;
12759 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
12760 extra_index += 1;
12761 const body = sema.code.bodySlice(extra_index, info.body_len);
12762 extra_index += info.body_len;
12763
12764 const item = case_vals.items[scalar_i];
12765 const item_val = sema.resolveConstDefinedValue(child_block, .unneeded, item, undefined) catch unreachable;
12766 if (operand_val.eql(item_val, operand_ty, sema.mod)) {
12767 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
12768 return spa.resolveProngComptime(
12769 child_block,
12770 .normal,
12771 body,
12772 info.capture,
12773 .{ .scalar_capture = @intCast(scalar_i) },
12774 &.{item},
12775 if (info.is_inline) cond_operand else .none,
12776 info.has_tag_capture,
12777 merges,
12778 );
12779 }
12780 }
12781 }
12782 {
12783 var multi_i: usize = 0;
12784 var case_val_idx: usize = scalar_cases_len;
12785 while (multi_i < multi_cases_len) : (multi_i += 1) {
12786 const items_len = sema.code.extra[extra_index];
12787 extra_index += 1;
12788 const ranges_len = sema.code.extra[extra_index];
12789 extra_index += 1;
12790 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
12791 extra_index += 1 + items_len;
12792 const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len);
12793
12794 const items = case_vals.items[case_val_idx..][0..items_len];
12795 case_val_idx += items_len;
12796
12797 for (items) |item| {
12798 // Validation above ensured these will succeed.
12799 const item_val = sema.resolveConstDefinedValue(child_block, .unneeded, item, undefined) catch unreachable;
12800 if (operand_val.eql(item_val, operand_ty, sema.mod)) {
12801 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
12802 return spa.resolveProngComptime(
12803 child_block,
12804 .normal,
12805 body,
12806 info.capture,
12807 .{ .multi_capture = @intCast(multi_i) },
12808 items,
12809 if (info.is_inline) cond_operand else .none,
12810 info.has_tag_capture,
12811 merges,
12812 );
12813 }
12814 }
12815
12816 var range_i: usize = 0;
12817 while (range_i < ranges_len) : (range_i += 1) {
12818 const range_items = case_vals.items[case_val_idx..][0..2];
12819 extra_index += 2;
12820 case_val_idx += 2;
12821
12822 // Validation above ensured these will succeed.
12823 const first_val = sema.resolveConstDefinedValue(child_block, .unneeded, range_items[0], undefined) catch unreachable;
12824 const last_val = sema.resolveConstDefinedValue(child_block, .unneeded, range_items[1], undefined) catch unreachable;
12825 if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and
12826 (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty)))
12827 {
12828 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
12829 return spa.resolveProngComptime(
12830 child_block,
12831 .normal,
12832 body,
12833 info.capture,
12834 .{ .multi_capture = @intCast(multi_i) },
12835 undefined, // case_vals may be undefined for ranges
12836 if (info.is_inline) cond_operand else .none,
12837 info.has_tag_capture,
12838 merges,
12839 );
12840 }
12841 }
12842
12843 extra_index += info.body_len;
12844 }
12845 }
12846 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special.body, cond_operand);
12847 if (empty_enum) {
12848 return .void_value;
12849 }
12850
12851 return spa.resolveProngComptime(
12852 child_block,
12853 .special,
12854 special.body,
12855 special.capture,
12856 .special_capture,
12857 undefined, // case_vals may be undefined for special prongs
12858 if (special.is_inline) cond_operand else .none,
12859 special.has_tag_capture,
12860 merges,
12861 );
12602}12862}
1260312863
12604const RangeSetUnhandledIterator = struct {12864const RangeSetUnhandledIterator = struct {
...@@ -12718,6 +12978,168 @@ fn resolveSwitchItemVal(...@@ -12718,6 +12978,168 @@ fn resolveSwitchItemVal(
12718 return .{ .ref = new_item, .val = val.toIntern() };12978 return .{ .ref = new_item, .val = val.toIntern() };
12719}12979}
1272012980
12981fn validateErrSetSwitch(
12982 sema: *Sema,
12983 block: *Block,
12984 seen_errors: *SwitchErrorSet,
12985 case_vals: *std.ArrayListUnmanaged(Air.Inst.Ref),
12986 operand_ty: Type,
12987 inst_data: std.meta.FieldType(Zir.Inst.Data, .pl_node),
12988 scalar_cases_len: u32,
12989 multi_cases_len: u32,
12990 else_case: struct { body: []const Zir.Inst.Index, end: usize, src: LazySrcLoc },
12991 has_else: bool,
12992) CompileError!?Type {
12993 const gpa = sema.gpa;
12994 const mod = sema.mod;
12995 const ip = &mod.intern_pool;
12996
12997 const src_node_offset = inst_data.src_node;
12998 const src = inst_data.src();
12999
13000 var extra_index: usize = else_case.end;
13001 {
13002 var scalar_i: u32 = 0;
13003 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
13004 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
13005 extra_index += 1;
13006 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
13007 extra_index += 1 + info.body_len;
13008
13009 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
13010 block,
13011 seen_errors,
13012 item_ref,
13013 operand_ty,
13014 src_node_offset,
13015 .{ .scalar = scalar_i },
13016 ));
13017 }
13018 }
13019 {
13020 var multi_i: u32 = 0;
13021 while (multi_i < multi_cases_len) : (multi_i += 1) {
13022 const items_len = sema.code.extra[extra_index];
13023 extra_index += 1;
13024 const ranges_len = sema.code.extra[extra_index];
13025 extra_index += 1;
13026 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
13027 extra_index += 1;
13028 const items = sema.code.refSlice(extra_index, items_len);
13029 extra_index += items_len + info.body_len;
13030
13031 try case_vals.ensureUnusedCapacity(gpa, items.len);
13032 for (items, 0..) |item_ref, item_i| {
13033 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
13034 block,
13035 seen_errors,
13036 item_ref,
13037 operand_ty,
13038 src_node_offset,
13039 .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } },
13040 ));
13041 }
13042
13043 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
13044 }
13045 }
13046
13047 switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) {
13048 .anyerror_type => {
13049 if (!has_else) {
13050 return sema.fail(
13051 block,
13052 src,
13053 "else prong required when switching on type 'anyerror'",
13054 .{},
13055 );
13056 }
13057 return Type.anyerror;
13058 },
13059 else => |err_set_ty_index| else_validation: {
13060 const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names;
13061 var maybe_msg: ?*Module.ErrorMsg = null;
13062 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
13063
13064 for (error_names.get(ip)) |error_name| {
13065 if (!seen_errors.contains(error_name) and !has_else) {
13066 const msg = maybe_msg orelse blk: {
13067 maybe_msg = try sema.errMsg(
13068 block,
13069 src,
13070 "switch must handle all possibilities",
13071 .{},
13072 );
13073 break :blk maybe_msg.?;
13074 };
13075
13076 try sema.errNote(
13077 block,
13078 src,
13079 msg,
13080 "unhandled error value: 'error.{}'",
13081 .{error_name.fmt(ip)},
13082 );
13083 }
13084 }
13085
13086 if (maybe_msg) |msg| {
13087 maybe_msg = null;
13088 try sema.addDeclaredHereNote(msg, operand_ty);
13089 return sema.failWithOwnedErrorMsg(block, msg);
13090 }
13091
13092 if (has_else and seen_errors.count() == error_names.len) {
13093 // In order to enable common patterns for generic code allow simple else bodies
13094 // else => unreachable,
13095 // else => return,
13096 // else => |e| return e,
13097 // even if all the possible errors were already handled.
13098 const tags = sema.code.instructions.items(.tag);
13099 for (else_case.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) {
13100 .dbg_block_begin,
13101 .dbg_block_end,
13102 .dbg_stmt,
13103 .dbg_var_val,
13104 .ret_type,
13105 .as_node,
13106 .ret_node,
13107 .@"unreachable",
13108 .@"defer",
13109 .defer_err_code,
13110 .err_union_code,
13111 .ret_err_value_code,
13112 .restore_err_ret_index,
13113 .is_non_err,
13114 .ret_is_non_err,
13115 .condbr,
13116 => {},
13117 else => break,
13118 } else break :else_validation;
13119
13120 return sema.fail(
13121 block,
13122 else_case.src,
13123 "unreachable else prong; all cases already handled",
13124 .{},
13125 );
13126 }
13127
13128 var names: InferredErrorSet.NameMap = .{};
13129 try names.ensureUnusedCapacity(sema.arena, error_names.len);
13130 for (error_names.get(ip)) |error_name| {
13131 if (seen_errors.contains(error_name)) continue;
13132
13133 names.putAssumeCapacityNoClobber(error_name, {});
13134 }
13135 // No need to keep the hash map metadata correct; here we
13136 // extract the (sorted) keys only.
13137 return try mod.errorSetFromUnsortedNames(names.keys());
13138 },
13139 }
13140 return null;
13141}
13142
12721fn validateSwitchRange(13143fn validateSwitchRange(
12722 sema: *Sema,13144 sema: *Sema,
12723 block: *Block,13145 block: *Block,
...@@ -12905,7 +13327,14 @@ fn validateSwitchNoRange(...@@ -12905,7 +13327,14 @@ fn validateSwitchNoRange(
12905 return sema.failWithOwnedErrorMsg(block, msg);13327 return sema.failWithOwnedErrorMsg(block, msg);
12906}13328}
1290713329
12908fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref, operand_src: LazySrcLoc) !bool {13330fn maybeErrorUnwrap(
13331 sema: *Sema,
13332 block: *Block,
13333 body: []const Zir.Inst.Index,
13334 operand: Air.Inst.Ref,
13335 operand_src: LazySrcLoc,
13336 allow_err_code_inst: bool,
13337) !bool {
12909 const mod = sema.mod;13338 const mod = sema.mod;
12910 if (!mod.backendSupportsFeature(.panic_unwrap_error)) return false;13339 if (!mod.backendSupportsFeature(.panic_unwrap_error)) return false;
1291113340
...@@ -12913,6 +13342,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op...@@ -12913,6 +13342,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op
12913 for (body) |inst| {13342 for (body) |inst| {
12914 switch (tags[@intFromEnum(inst)]) {13343 switch (tags[@intFromEnum(inst)]) {
12915 .@"unreachable" => if (!block.wantSafety()) return false,13344 .@"unreachable" => if (!block.wantSafety()) return false,
13345 .err_union_code => if (!allow_err_code_inst) return false,
12916 .save_err_ret_index,13346 .save_err_ret_index,
12917 .dbg_block_begin,13347 .dbg_block_begin,
12918 .dbg_block_end,13348 .dbg_block_end,
...@@ -12930,6 +13360,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op...@@ -12930,6 +13360,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op
12930 const air_inst = switch (tags[@intFromEnum(inst)]) {13360 const air_inst = switch (tags[@intFromEnum(inst)]) {
12931 .dbg_block_begin,13361 .dbg_block_begin,
12932 .dbg_block_end,13362 .dbg_block_end,
13363 .err_union_code,
12933 => continue,13364 => continue,
12934 .dbg_stmt => {13365 .dbg_stmt => {
12935 try sema.zirDbgStmt(block, inst);13366 try sema.zirDbgStmt(block, inst);
...@@ -18393,7 +18824,7 @@ fn zirCondbr(...@@ -18393,7 +18824,7 @@ fn zirCondbr(
18393 break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand);18824 break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand);
18394 };18825 };
1839518826
18396 if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src)) {18827 if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src, false)) {
18397 // nothing to do18828 // nothing to do
18398 } else {18829 } else {
18399 try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);18830 try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
src/Zir.zig+31-7
...@@ -100,6 +100,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) {...@@ -100,6 +100,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) {
100 Inst.Call.Flags,100 Inst.Call.Flags,
101 Inst.BuiltinCall.Flags,101 Inst.BuiltinCall.Flags,
102 Inst.SwitchBlock.Bits,102 Inst.SwitchBlock.Bits,
103 Inst.SwitchBlockErrUnion.Bits,
103 Inst.FuncFancy.Bits,104 Inst.FuncFancy.Bits,
104 => @bitCast(code.extra[i]),105 => @bitCast(code.extra[i]),
105106
...@@ -277,9 +278,6 @@ pub const Inst = struct {...@@ -277,9 +278,6 @@ pub const Inst = struct {
277 /// Create a `anyframe->T` type.278 /// Create a `anyframe->T` type.
278 /// Uses the `un_node` field.279 /// Uses the `un_node` field.
279 anyframe_type,280 anyframe_type,
280 /// Type coercion. No source location attached.
281 /// Uses the `bin` field.
282 as,
283 /// Type coercion to the function's return type.281 /// Type coercion to the function's return type.
284 /// Uses the `pl_node` field. Payload is `As`. AST node could be many things.282 /// Uses the `pl_node` field. Payload is `As`. AST node could be many things.
285 as_node,283 as_node,
...@@ -688,6 +686,9 @@ pub const Inst = struct {...@@ -688,6 +686,9 @@ pub const Inst = struct {
688 /// A switch expression. Uses the `pl_node` union field.686 /// A switch expression. Uses the `pl_node` union field.
689 /// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer.687 /// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer.
690 switch_block_ref,688 switch_block_ref,
689 /// A switch on an error union `a catch |err| switch (err) {...}`.
690 /// Uses the `pl_node` union field. AST node is the `catch`, payload is `SwitchBlockErrUnion`.
691 switch_block_err_union,
691 /// Check that operand type supports the dereference operand (.*).692 /// Check that operand type supports the dereference operand (.*).
692 /// Uses the `un_node` field.693 /// Uses the `un_node` field.
693 validate_deref,694 validate_deref,
...@@ -1083,7 +1084,6 @@ pub const Inst = struct {...@@ -1083,7 +1084,6 @@ pub const Inst = struct {
1083 .vector_elem_type,1084 .vector_elem_type,
1084 .indexable_ptr_len,1085 .indexable_ptr_len,
1085 .anyframe_type,1086 .anyframe_type,
1086 .as,
1087 .as_node,1087 .as_node,
1088 .as_shift_operand,1088 .as_shift_operand,
1089 .bit_and,1089 .bit_and,
...@@ -1190,6 +1190,7 @@ pub const Inst = struct {...@@ -1190,6 +1190,7 @@ pub const Inst = struct {
1190 .set_eval_branch_quota,1190 .set_eval_branch_quota,
1191 .switch_block,1191 .switch_block,
1192 .switch_block_ref,1192 .switch_block_ref,
1193 .switch_block_err_union,
1193 .validate_deref,1194 .validate_deref,
1194 .validate_destructure,1195 .validate_destructure,
1195 .union_init,1196 .union_init,
...@@ -1396,7 +1397,6 @@ pub const Inst = struct {...@@ -1396,7 +1397,6 @@ pub const Inst = struct {
1396 .vector_elem_type,1397 .vector_elem_type,
1397 .indexable_ptr_len,1398 .indexable_ptr_len,
1398 .anyframe_type,1399 .anyframe_type,
1399 .as,
1400 .as_node,1400 .as_node,
1401 .as_shift_operand,1401 .as_shift_operand,
1402 .bit_and,1402 .bit_and,
...@@ -1488,6 +1488,7 @@ pub const Inst = struct {...@@ -1488,6 +1488,7 @@ pub const Inst = struct {
1488 .typeof_log2_int_type,1488 .typeof_log2_int_type,
1489 .switch_block,1489 .switch_block,
1490 .switch_block_ref,1490 .switch_block_ref,
1491 .switch_block_err_union,
1491 .union_init,1492 .union_init,
1492 .field_type_ref,1493 .field_type_ref,
1493 .enum_from_int,1494 .enum_from_int,
...@@ -1629,7 +1630,6 @@ pub const Inst = struct {...@@ -1629,7 +1630,6 @@ pub const Inst = struct {
1629 .vector_elem_type = .un_node,1630 .vector_elem_type = .un_node,
1630 .indexable_ptr_len = .un_node,1631 .indexable_ptr_len = .un_node,
1631 .anyframe_type = .un_node,1632 .anyframe_type = .un_node,
1632 .as = .bin,
1633 .as_node = .pl_node,1633 .as_node = .pl_node,
1634 .as_shift_operand = .pl_node,1634 .as_shift_operand = .pl_node,
1635 .bit_and = .pl_node,1635 .bit_and = .pl_node,
...@@ -1741,6 +1741,7 @@ pub const Inst = struct {...@@ -1741,6 +1741,7 @@ pub const Inst = struct {
1741 .enum_literal = .str_tok,1741 .enum_literal = .str_tok,
1742 .switch_block = .pl_node,1742 .switch_block = .pl_node,
1743 .switch_block_ref = .pl_node,1743 .switch_block_ref = .pl_node,
1744 .switch_block_err_union = .pl_node,
1744 .validate_deref = .un_node,1745 .validate_deref = .un_node,
1745 .validate_destructure = .pl_node,1746 .validate_destructure = .pl_node,
1746 .field_type_ref = .pl_node,1747 .field_type_ref = .pl_node,
...@@ -2782,6 +2783,29 @@ pub const Inst = struct {...@@ -2782,6 +2783,29 @@ pub const Inst = struct {
2782 index: u32,2783 index: u32,
2783 };2784 };
27842785
2786 pub const SwitchBlockErrUnion = struct {
2787 operand: Ref,
2788 bits: Bits,
2789 main_src_node_offset: i32,
2790
2791 pub const Bits = packed struct(u32) {
2792 /// If true, one or more prongs have multiple items.
2793 has_multi_cases: bool,
2794 /// If true, there is an else prong. This is mutually exclusive with `has_under`.
2795 has_else: bool,
2796 any_uses_err_capture: bool,
2797 payload_is_ref: bool,
2798 scalar_cases_len: ScalarCasesLen,
2799
2800 pub const ScalarCasesLen = u28;
2801 };
2802
2803 pub const MultiProng = struct {
2804 items: []const Ref,
2805 body: []const Index,
2806 };
2807 };
2808
2785 /// 0. multi_cases_len: u32 // If has_multi_cases is set.2809 /// 0. multi_cases_len: u32 // If has_multi_cases is set.
2786 /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture.2810 /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture.
2787 /// 2. else_body { // If has_else or has_under is set.2811 /// 2. else_body { // If has_else or has_under is set.
...@@ -2830,7 +2854,7 @@ pub const Inst = struct {...@@ -2830,7 +2854,7 @@ pub const Inst = struct {
2830 };2854 };
2831 };2855 };
28322856
2833 pub const Bits = packed struct {2857 pub const Bits = packed struct(u32) {
2834 /// If true, one or more prongs have multiple items.2858 /// If true, one or more prongs have multiple items.
2835 has_multi_cases: bool,2859 has_multi_cases: bool,
2836 /// If true, there is an else prong. This is mutually exclusive with `has_under`.2860 /// If true, there is an else prong. This is mutually exclusive with `has_under`.
src/print_zir.zig+139-1
...@@ -199,7 +199,6 @@ const Writer = struct {...@@ -199,7 +199,6 @@ const Writer = struct {
199 const tag = tags[@intFromEnum(inst)];199 const tag = tags[@intFromEnum(inst)];
200 try stream.print("= {s}(", .{@tagName(tags[@intFromEnum(inst)])});200 try stream.print("= {s}(", .{@tagName(tags[@intFromEnum(inst)])});
201 switch (tag) {201 switch (tag) {
202 .as,
203 .store,202 .store,
204 .store_to_inferred_ptr,203 .store_to_inferred_ptr,
205 => try self.writeBin(stream, inst),204 => try self.writeBin(stream, inst),
...@@ -465,6 +464,8 @@ const Writer = struct {...@@ -465,6 +464,8 @@ const Writer = struct {
465 .switch_block_ref,464 .switch_block_ref,
466 => try self.writeSwitchBlock(stream, inst),465 => try self.writeSwitchBlock(stream, inst),
467466
467 .switch_block_err_union => try self.writeSwitchBlockErrUnion(stream, inst),
468
468 .field_val,469 .field_val,
469 .field_ptr,470 .field_ptr,
470 => try self.writePlNodeField(stream, inst),471 => try self.writePlNodeField(stream, inst),
...@@ -2027,6 +2028,143 @@ const Writer = struct {...@@ -2027,6 +2028,143 @@ const Writer = struct {
2027 try self.writeSrc(stream, inst_data.src());2028 try self.writeSrc(stream, inst_data.src());
2028 }2029 }
20292030
2031 fn writeSwitchBlockErrUnion(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
2032 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
2033 const extra = self.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);
2034
2035 var extra_index: usize = extra.end;
2036
2037 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
2038 const multi_cases_len = self.code.extra[extra_index];
2039 extra_index += 1;
2040 break :blk multi_cases_len;
2041 } else 0;
2042
2043 const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: {
2044 const tag_capture_inst = self.code.extra[extra_index];
2045 extra_index += 1;
2046 break :blk @enumFromInt(tag_capture_inst);
2047 } else undefined;
2048
2049 try self.writeInstRef(stream, extra.data.operand);
2050
2051 if (extra.data.bits.any_uses_err_capture) {
2052 try stream.writeAll(", err_capture=");
2053 try self.writeInstIndex(stream, err_capture_inst);
2054 }
2055
2056 self.indent += 2;
2057
2058 {
2059 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
2060 extra_index += 1;
2061
2062 assert(!info.is_inline);
2063 const body = self.code.bodySlice(extra_index, info.body_len);
2064 extra_index += body.len;
2065
2066 try stream.writeAll(",\n");
2067 try stream.writeByteNTimes(' ', self.indent);
2068 try stream.writeAll("non_err => ");
2069 try self.writeBracedBody(stream, body);
2070 }
2071
2072 if (extra.data.bits.has_else) {
2073 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
2074 extra_index += 1;
2075 const capture_text = switch (info.capture) {
2076 .none => "",
2077 .by_val => "by_val ",
2078 .by_ref => "by_ref ",
2079 };
2080 const inline_text = if (info.is_inline) "inline " else "";
2081 const body = self.code.bodySlice(extra_index, info.body_len);
2082 extra_index += body.len;
2083
2084 try stream.writeAll(",\n");
2085 try stream.writeByteNTimes(' ', self.indent);
2086 try stream.print("{s}{s}else => ", .{ capture_text, inline_text });
2087 try self.writeBracedBody(stream, body);
2088 }
2089
2090 {
2091 const scalar_cases_len = extra.data.bits.scalar_cases_len;
2092 var scalar_i: usize = 0;
2093 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2094 const item_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
2095 extra_index += 1;
2096 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
2097 extra_index += 1;
2098 const body = self.code.bodySlice(extra_index, info.body_len);
2099 extra_index += info.body_len;
2100
2101 try stream.writeAll(",\n");
2102 try stream.writeByteNTimes(' ', self.indent);
2103 switch (info.capture) {
2104 .none => {},
2105 .by_val => try stream.writeAll("by_val "),
2106 .by_ref => try stream.writeAll("by_ref "),
2107 }
2108 if (info.is_inline) try stream.writeAll("inline ");
2109 try self.writeInstRef(stream, item_ref);
2110 try stream.writeAll(" => ");
2111 try self.writeBracedBody(stream, body);
2112 }
2113 }
2114 {
2115 var multi_i: usize = 0;
2116 while (multi_i < multi_cases_len) : (multi_i += 1) {
2117 const items_len = self.code.extra[extra_index];
2118 extra_index += 1;
2119 const ranges_len = self.code.extra[extra_index];
2120 extra_index += 1;
2121 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
2122 extra_index += 1;
2123 const items = self.code.refSlice(extra_index, items_len);
2124 extra_index += items_len;
2125
2126 try stream.writeAll(",\n");
2127 try stream.writeByteNTimes(' ', self.indent);
2128 switch (info.capture) {
2129 .none => {},
2130 .by_val => try stream.writeAll("by_val "),
2131 .by_ref => try stream.writeAll("by_ref "),
2132 }
2133 if (info.is_inline) try stream.writeAll("inline ");
2134
2135 for (items, 0..) |item_ref, item_i| {
2136 if (item_i != 0) try stream.writeAll(", ");
2137 try self.writeInstRef(stream, item_ref);
2138 }
2139
2140 var range_i: usize = 0;
2141 while (range_i < ranges_len) : (range_i += 1) {
2142 const item_first = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
2143 extra_index += 1;
2144 const item_last = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
2145 extra_index += 1;
2146
2147 if (range_i != 0 or items.len != 0) {
2148 try stream.writeAll(", ");
2149 }
2150 try self.writeInstRef(stream, item_first);
2151 try stream.writeAll("...");
2152 try self.writeInstRef(stream, item_last);
2153 }
2154
2155 const body = self.code.bodySlice(extra_index, info.body_len);
2156 extra_index += info.body_len;
2157 try stream.writeAll(" => ");
2158 try self.writeBracedBody(stream, body);
2159 }
2160 }
2161
2162 self.indent -= 2;
2163
2164 try stream.writeAll(") ");
2165 try self.writeSrc(stream, inst_data.src());
2166 }
2167
2030 fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {2168 fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
2031 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;2169 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
2032 const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);2170 const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
test/behavior/switch_on_captured_error.zig created+750
...@@ -0,0 +1,750 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const expect = std.testing.expect;
4const expectError = std.testing.expectError;
5const expectEqual = std.testing.expectEqual;
6
7test "switch on error union catch capture" {
8 const S = struct {
9 const Error = error{ A, B, C };
10 fn doTheTest() !void {
11 try testScalar();
12 try testMulti();
13 try testElse();
14 try testCapture();
15 try testInline();
16 try testEmptyErrSet();
17 }
18
19 fn testScalar() !void {
20 {
21 var a: Error!u64 = 3;
22 _ = &a;
23 const b: u64 = a catch |err| switch (err) {
24 error.A => 0,
25 error.B => 1,
26 error.C => 2,
27 };
28 try expectEqual(@as(u64, 3), b);
29 }
30 {
31 var a: Error!u64 = 3;
32 _ = &a;
33 const b: u64 = a catch |err| switch (err) {
34 error.A => 0,
35 error.B => @intFromError(err) + 4,
36 error.C => @intFromError(err) + 4,
37 };
38 try expectEqual(@as(u64, 3), b);
39 }
40 {
41 var a: Error!u64 = error.A;
42 _ = &a;
43 const b: u64 = a catch |err| switch (err) {
44 error.A => 0,
45 error.B => @intFromError(err) + 4,
46 error.C => @intFromError(err) + 4,
47 };
48 try expectEqual(@as(u64, 0), b);
49 }
50 }
51
52 fn testMulti() !void {
53 {
54 var a: Error!u64 = 3;
55 _ = &a;
56 const b: u64 = a catch |err| switch (err) {
57 error.A, error.B => 0,
58 error.C => @intFromError(err) + 4,
59 };
60 try expectEqual(@as(u64, 3), b);
61 }
62 {
63 var a: Error!u64 = 3;
64 _ = &a;
65 const b: u64 = a catch |err| switch (err) {
66 error.A => 0,
67 error.B, error.C => @intFromError(err) + 4,
68 };
69 try expectEqual(@as(u64, 3), b);
70 }
71 {
72 var a: Error!u64 = error.A;
73 _ = &a;
74 const b: u64 = a catch |err| switch (err) {
75 error.A, error.B => 0,
76 error.C => @intFromError(err) + 4,
77 };
78 try expectEqual(@as(u64, 0), b);
79 }
80 {
81 var a: Error!u64 = error.A;
82 _ = &a;
83 const b: u64 = a catch |err| switch (err) {
84 error.A => 0,
85 error.B, error.C => @intFromError(err) + 4,
86 };
87 try expectEqual(@as(u64, 0), b);
88 }
89 {
90 var a: Error!u64 = error.B;
91 _ = &a;
92 const b: u64 = a catch |err| switch (err) {
93 error.A => 0,
94 error.B, error.C => @intFromError(err) + 4,
95 };
96 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
97 }
98 }
99
100 fn testElse() !void {
101 {
102 var a: Error!u64 = 3;
103 _ = &a;
104 const b: u64 = a catch |err| switch (err) {
105 error.A => 0,
106 else => 1,
107 };
108 try expectEqual(@as(u64, 3), b);
109 }
110 {
111 var a: Error!u64 = 3;
112 _ = &a;
113 const b: u64 = a catch |err| switch (err) {
114 error.A => 0,
115 else => @intFromError(err) + 4,
116 };
117 try expectEqual(@as(u64, 3), b);
118 }
119 {
120 var a: Error!u64 = error.A;
121 _ = &a;
122 const b: u64 = a catch |err| switch (err) {
123 error.A => 1,
124 else => @intFromError(err) + 4,
125 };
126 try expectEqual(@as(u64, 1), b);
127 }
128 {
129 var a: Error!u64 = error.B;
130 _ = &a;
131 const b: u64 = a catch |err| switch (err) {
132 error.A => 0,
133 else => 1,
134 };
135 try expectEqual(@as(u64, 1), b);
136 }
137 {
138 var a: Error!u64 = error.B;
139 _ = &a;
140 const b: u64 = a catch |err| switch (err) {
141 error.A => 0,
142 else => @intFromError(err) + 4,
143 };
144 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
145 }
146 }
147
148 fn testCapture() !void {
149 {
150 var a: Error!u64 = error.A;
151 _ = &a;
152 const b: u64 = a catch |err| switch (err) {
153 error.A => |e| @intFromError(e) + 4,
154 else => 0,
155 };
156 try expectEqual(@as(u64, @intFromError(error.A) + 4), b);
157 }
158 {
159 var a: Error!u64 = error.A;
160 _ = &a;
161 const b: u64 = a catch |err| switch (err) {
162 error.A => 0,
163 else => |e| @intFromError(e) + 4,
164 };
165 try expectEqual(@as(u64, 0), b);
166 }
167 {
168 var a: Error!u64 = error.B;
169 _ = &a;
170 const b: u64 = a catch |err| switch (err) {
171 error.A => 0,
172 else => |e| @intFromError(e) + 4,
173 };
174 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
175 }
176 {
177 var a: Error!u64 = error.B;
178 _ = &a;
179 const b: u64 = a catch |err| switch (err) {
180 error.A => |e| @intFromError(e) + 4,
181 else => |e| @intFromError(e) + 4,
182 };
183 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
184 }
185 {
186 var a: Error!u64 = error.B;
187 _ = &a;
188 const b: u64 = a catch |err| switch (err) {
189 error.A => 0,
190 error.B, error.C => |e| @intFromError(e) + 4,
191 };
192 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
193 }
194 }
195
196 fn testInline() !void {
197 {
198 var a: Error!u64 = error.B;
199 _ = &a;
200 const b: u64 = a catch |err| switch (err) {
201 error.A => 0,
202 inline else => @intFromError(err) + 4,
203 };
204 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
205 }
206 {
207 var a: Error!u64 = error.B;
208 _ = &a;
209 const b: u64 = a catch |err| switch (err) {
210 error.A => |e| @intFromError(e) + 4,
211 inline else => @intFromError(err) + 4,
212 };
213 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
214 }
215 {
216 var a: Error!u64 = error.B;
217 _ = &a;
218 const b: u64 = a catch |err| switch (err) {
219 inline else => |e| @intFromError(e) + 4,
220 };
221 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
222 }
223 {
224 var a: Error!u64 = error.B;
225 _ = &a;
226 const b: u64 = a catch |err| switch (err) {
227 error.A => 0,
228 inline error.B, error.C => |e| @intFromError(e) + 4,
229 };
230 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
231 }
232 }
233
234 fn testEmptyErrSet() !void {
235 {
236 var a: error{}!u64 = 0;
237 _ = &a;
238 const b: u64 = a catch |err| switch (err) {
239 else => |e| return e,
240 };
241 try expectEqual(@as(u64, 0), b);
242 }
243 {
244 var a: error{}!u64 = 0;
245 _ = &a;
246 const b: u64 = a catch |err| switch (err) {
247 error.UnknownError => return error.Fail,
248 else => |e| return e,
249 };
250 try expectEqual(@as(u64, 0), b);
251 }
252 }
253 };
254
255 try comptime S.doTheTest();
256 try S.doTheTest();
257}
258
259test "switch on error union if else capture" {
260 const S = struct {
261 const Error = error{ A, B, C };
262 fn doTheTest() !void {
263 try testScalar();
264 try testScalarPtr();
265 try testMulti();
266 try testMultiPtr();
267 try testElse();
268 try testElsePtr();
269 try testCapture();
270 try testCapturePtr();
271 try testInline();
272 try testInlinePtr();
273 try testEmptyErrSet();
274 try testEmptyErrSetPtr();
275 }
276
277 fn testScalar() !void {
278 {
279 var a: Error!u64 = 3;
280 _ = &a;
281 const b: u64 = if (a) |x| x else |err| switch (err) {
282 error.A => 0,
283 error.B => 1,
284 error.C => 2,
285 };
286 try expectEqual(@as(u64, 3), b);
287 }
288 {
289 var a: Error!u64 = 3;
290 _ = &a;
291 const b: u64 = if (a) |x| x else |err| switch (err) {
292 error.A => 0,
293 error.B => @intFromError(err) + 4,
294 error.C => @intFromError(err) + 4,
295 };
296 try expectEqual(@as(u64, 3), b);
297 }
298 {
299 var a: Error!u64 = error.A;
300 _ = &a;
301 const b: u64 = if (a) |x| x else |err| switch (err) {
302 error.A => 0,
303 error.B => @intFromError(err) + 4,
304 error.C => @intFromError(err) + 4,
305 };
306 try expectEqual(@as(u64, 0), b);
307 }
308 }
309
310 fn testScalarPtr() !void {
311 {
312 var a: Error!u64 = 3;
313 _ = &a;
314 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
315 error.A => 0,
316 error.B => 1,
317 error.C => 2,
318 };
319 try expectEqual(@as(u64, 3), b);
320 }
321 {
322 var a: Error!u64 = 3;
323 _ = &a;
324 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
325 error.A => 0,
326 error.B => @intFromError(err) + 4,
327 error.C => @intFromError(err) + 4,
328 };
329 try expectEqual(@as(u64, 3), b);
330 }
331 {
332 var a: Error!u64 = error.A;
333 _ = &a;
334 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
335 error.A => 0,
336 error.B => @intFromError(err) + 4,
337 error.C => @intFromError(err) + 4,
338 };
339 try expectEqual(@as(u64, 0), b);
340 }
341 }
342
343 fn testMulti() !void {
344 {
345 var a: Error!u64 = 3;
346 _ = &a;
347 const b: u64 = if (a) |x| x else |err| switch (err) {
348 error.A, error.B => 0,
349 error.C => @intFromError(err) + 4,
350 };
351 try expectEqual(@as(u64, 3), b);
352 }
353 {
354 var a: Error!u64 = 3;
355 _ = &a;
356 const b: u64 = if (a) |x| x else |err| switch (err) {
357 error.A => 0,
358 error.B, error.C => @intFromError(err) + 4,
359 };
360 try expectEqual(@as(u64, 3), b);
361 }
362 {
363 var a: Error!u64 = error.A;
364 _ = &a;
365 const b: u64 = if (a) |x| x else |err| switch (err) {
366 error.A, error.B => 0,
367 error.C => @intFromError(err) + 4,
368 };
369 try expectEqual(@as(u64, 0), b);
370 }
371 {
372 var a: Error!u64 = error.A;
373 _ = &a;
374 const b: u64 = if (a) |x| x else |err| switch (err) {
375 error.A => 0,
376 error.B, error.C => @intFromError(err) + 4,
377 };
378 try expectEqual(@as(u64, 0), b);
379 }
380 {
381 var a: Error!u64 = error.B;
382 _ = &a;
383 const b: u64 = if (a) |x| x else |err| switch (err) {
384 error.A => 0,
385 error.B, error.C => @intFromError(err) + 4,
386 };
387 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
388 }
389 }
390
391 fn testMultiPtr() !void {
392 {
393 var a: Error!u64 = 3;
394 _ = &a;
395 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
396 error.A, error.B => 0,
397 error.C => @intFromError(err) + 4,
398 };
399 try expectEqual(@as(u64, 3), b);
400 }
401 {
402 var a: Error!u64 = 3;
403 _ = &a;
404 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
405 error.A => 0,
406 error.B, error.C => @intFromError(err) + 4,
407 };
408 try expectEqual(@as(u64, 3), b);
409 }
410 {
411 var a: Error!u64 = error.A;
412 _ = &a;
413 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
414 error.A, error.B => 0,
415 error.C => @intFromError(err) + 4,
416 };
417 try expectEqual(@as(u64, 0), b);
418 }
419 {
420 var a: Error!u64 = error.A;
421 _ = &a;
422 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
423 error.A => 0,
424 error.B, error.C => @intFromError(err) + 4,
425 };
426 try expectEqual(@as(u64, 0), b);
427 }
428 {
429 var a: Error!u64 = error.B;
430 _ = &a;
431 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
432 error.A => 0,
433 error.B, error.C => @intFromError(err) + 4,
434 };
435 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
436 }
437 }
438
439 fn testElse() !void {
440 {
441 var a: Error!u64 = 3;
442 _ = &a;
443 const b: u64 = if (a) |x| x else |err| switch (err) {
444 error.A => 0,
445 else => 1,
446 };
447 try expectEqual(@as(u64, 3), b);
448 }
449 {
450 var a: Error!u64 = 3;
451 _ = &a;
452 const b: u64 = if (a) |x| x else |err| switch (err) {
453 error.A => 0,
454 else => @intFromError(err) + 4,
455 };
456 try expectEqual(@as(u64, 3), b);
457 }
458 {
459 var a: Error!u64 = error.A;
460 _ = &a;
461 const b: u64 = if (a) |x| x else |err| switch (err) {
462 error.A => 1,
463 else => @intFromError(err) + 4,
464 };
465 try expectEqual(@as(u64, 1), b);
466 }
467 {
468 var a: Error!u64 = error.B;
469 _ = &a;
470 const b: u64 = if (a) |x| x else |err| switch (err) {
471 error.A => 0,
472 else => 1,
473 };
474 try expectEqual(@as(u64, 1), b);
475 }
476 {
477 var a: Error!u64 = error.B;
478 _ = &a;
479 const b: u64 = if (a) |x| x else |err| switch (err) {
480 error.A => 0,
481 else => @intFromError(err) + 4,
482 };
483 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
484 }
485 }
486
487 fn testElsePtr() !void {
488 {
489 var a: Error!u64 = 3;
490 _ = &a;
491 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
492 error.A => 0,
493 else => 1,
494 };
495 try expectEqual(@as(u64, 3), b);
496 }
497 {
498 var a: Error!u64 = 3;
499 _ = &a;
500 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
501 error.A => 0,
502 else => @intFromError(err) + 4,
503 };
504 try expectEqual(@as(u64, 3), b);
505 }
506 {
507 var a: Error!u64 = error.A;
508 _ = &a;
509 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
510 error.A => 1,
511 else => @intFromError(err) + 4,
512 };
513 try expectEqual(@as(u64, 1), b);
514 }
515 {
516 var a: Error!u64 = error.B;
517 _ = &a;
518 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
519 error.A => 0,
520 else => 1,
521 };
522 try expectEqual(@as(u64, 1), b);
523 }
524 {
525 var a: Error!u64 = error.B;
526 _ = &a;
527 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
528 error.A => 0,
529 else => @intFromError(err) + 4,
530 };
531 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
532 }
533 }
534
535 fn testCapture() !void {
536 {
537 var a: Error!u64 = error.A;
538 _ = &a;
539 const b: u64 = if (a) |x| x else |err| switch (err) {
540 error.A => |e| @intFromError(e) + 4,
541 else => 0,
542 };
543 try expectEqual(@as(u64, @intFromError(error.A) + 4), b);
544 }
545 {
546 var a: Error!u64 = error.A;
547 _ = &a;
548 const b: u64 = if (a) |x| x else |err| switch (err) {
549 error.A => 0,
550 else => |e| @intFromError(e) + 4,
551 };
552 try expectEqual(@as(u64, 0), b);
553 }
554 {
555 var a: Error!u64 = error.B;
556 _ = &a;
557 const b: u64 = if (a) |x| x else |err| switch (err) {
558 error.A => 0,
559 else => |e| @intFromError(e) + 4,
560 };
561 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
562 }
563 {
564 var a: Error!u64 = error.B;
565 _ = &a;
566 const b: u64 = if (a) |x| x else |err| switch (err) {
567 error.A => |e| @intFromError(e) + 4,
568 else => |e| @intFromError(e) + 4,
569 };
570 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
571 }
572 {
573 var a: Error!u64 = error.B;
574 _ = &a;
575 const b: u64 = if (a) |x| x else |err| switch (err) {
576 error.A => 0,
577 error.B, error.C => |e| @intFromError(e) + 4,
578 };
579 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
580 }
581 }
582
583 fn testCapturePtr() !void {
584 {
585 var a: Error!u64 = error.A;
586 _ = &a;
587 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
588 error.A => |e| @intFromError(e) + 4,
589 else => 0,
590 };
591 try expectEqual(@as(u64, @intFromError(error.A) + 4), b);
592 }
593 {
594 var a: Error!u64 = error.A;
595 _ = &a;
596 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
597 error.A => 0,
598 else => |e| @intFromError(e) + 4,
599 };
600 try expectEqual(@as(u64, 0), b);
601 }
602 {
603 var a: Error!u64 = error.B;
604 _ = &a;
605 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
606 error.A => 0,
607 else => |e| @intFromError(e) + 4,
608 };
609 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
610 }
611 {
612 var a: Error!u64 = error.B;
613 _ = &a;
614 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
615 error.A => |e| @intFromError(e) + 4,
616 else => |e| @intFromError(e) + 4,
617 };
618 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
619 }
620 {
621 var a: Error!u64 = error.B;
622 _ = &a;
623 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
624 error.A => 0,
625 error.B, error.C => |e| @intFromError(e) + 4,
626 };
627 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
628 }
629 }
630
631 fn testInline() !void {
632 {
633 var a: Error!u64 = error.B;
634 _ = &a;
635 const b: u64 = if (a) |x| x else |err| switch (err) {
636 error.A => 0,
637 inline else => @intFromError(err) + 4,
638 };
639 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
640 }
641 {
642 var a: Error!u64 = error.B;
643 _ = &a;
644 const b: u64 = if (a) |x| x else |err| switch (err) {
645 error.A => |e| @intFromError(e) + 4,
646 inline else => @intFromError(err) + 4,
647 };
648 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
649 }
650 {
651 var a: Error!u64 = error.B;
652 _ = &a;
653 const b: u64 = if (a) |x| x else |err| switch (err) {
654 inline else => |e| @intFromError(e) + 4,
655 };
656 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
657 }
658 {
659 var a: Error!u64 = error.B;
660 _ = &a;
661 const b: u64 = if (a) |x| x else |err| switch (err) {
662 error.A => 0,
663 inline error.B, error.C => |e| @intFromError(e) + 4,
664 };
665 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
666 }
667 }
668
669 fn testInlinePtr() !void {
670 {
671 var a: Error!u64 = error.B;
672 _ = &a;
673 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
674 error.A => 0,
675 inline else => @intFromError(err) + 4,
676 };
677 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
678 }
679 {
680 var a: Error!u64 = error.B;
681 _ = &a;
682 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
683 error.A => |e| @intFromError(e) + 4,
684 inline else => @intFromError(err) + 4,
685 };
686 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
687 }
688 {
689 var a: Error!u64 = error.B;
690 _ = &a;
691 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
692 inline else => |e| @intFromError(e) + 4,
693 };
694 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
695 }
696 {
697 var a: Error!u64 = error.B;
698 _ = &a;
699 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
700 error.A => 0,
701 inline error.B, error.C => |e| @intFromError(e) + 4,
702 };
703 try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
704 }
705 }
706
707 fn testEmptyErrSet() !void {
708 {
709 var a: error{}!u64 = 0;
710 _ = &a;
711 const b: u64 = if (a) |x| x else |err| switch (err) {
712 else => |e| return e,
713 };
714 try expectEqual(@as(u64, 0), b);
715 }
716 {
717 var a: error{}!u64 = 0;
718 _ = &a;
719 const b: u64 = if (a) |x| x else |err| switch (err) {
720 error.UnknownError => return error.Fail,
721 else => |e| return e,
722 };
723 try expectEqual(@as(u64, 0), b);
724 }
725 }
726
727 fn testEmptyErrSetPtr() !void {
728 {
729 var a: error{}!u64 = 0;
730 _ = &a;
731 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
732 else => |e| return e,
733 };
734 try expectEqual(@as(u64, 0), b);
735 }
736 {
737 var a: error{}!u64 = 0;
738 _ = &a;
739 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
740 error.UnknownError => return error.Fail,
741 else => |e| return e,
742 };
743 try expectEqual(@as(u64, 0), b);
744 }
745 }
746 };
747
748 try comptime S.doTheTest();
749 try S.doTheTest();
750}
test/cases/compile_errors/switch_expression-duplicate_error_prong.zig created+33
...@@ -0,0 +1,33 @@
1fn f(n: Error!i32) i32 {
2 if (n) |x|
3 _ = x
4 else |e| switch (e) {
5 error.Foo => 1,
6 error.Bar => 2,
7 error.Baz => 3,
8 error.Foo => 2,
9 }
10}
11fn g(n: Error!i32) i32 {
12 n catch |e| switch (e) {
13 error.Foo => 1,
14 error.Bar => 2,
15 error.Baz => 3,
16 error.Foo => 2,
17 };
18}
19
20const Error = error{ Foo, Bar, Baz };
21
22export fn entry() usize {
23 return @sizeOf(@TypeOf(&f)) + @sizeOf(@TypeOf(&g));
24}
25
26// error
27// backend=stage2
28// target=native
29//
30// :8:9: error: duplicate switch value
31// :5:9: note: previous value here
32// :16:9: error: duplicate switch value
33// :13:9: note: previous value here
test/cases/compile_errors/switch_expression-duplicate_error_prong_when_else_present.zig created+35
...@@ -0,0 +1,35 @@
1fn f(n: Error!i32) i32 {
2 if (n) |x|
3 _ = x
4 else |e| switch (e) {
5 error.Foo => 1,
6 error.Bar => 2,
7 error.Baz => 3,
8 error.Foo => 2,
9 else => 10,
10 }
11}
12fn g(n: Error!i32) i32 {
13 n catch |e| switch (e) {
14 error.Foo => 1,
15 error.Bar => 2,
16 error.Baz => 3,
17 error.Foo => 2,
18 else => 10,
19 };
20}
21
22const Error = error{ Foo, Bar, Baz };
23
24export fn entry() usize {
25 return @sizeOf(@TypeOf(&f)) + @sizeOf(@TypeOf(&g));
26}
27
28// error
29// backend=stage2
30// target=native
31//
32// :8:9: error: duplicate switch value
33// :5:9: note: previous value here
34// :17:9: error: duplicate switch value
35// :14:9: note: previous value here
test/cases/compile_errors/switch_expression-missing_error_prong.zig created+33
...@@ -0,0 +1,33 @@
1const Error = error {
2 One,
3 Two,
4 Three,
5 Four,
6};
7fn f(n: Error!i32) i32 {
8 if (n) |x| x else |e| switch (e) {
9 error.One => 1,
10 error.Two => 2,
11 error.Three => 3,
12 }
13}
14fn h(n: Error!i32) i32 {
15 n catch |e| switch (e) {
16 error.One => 1,
17 error.Two => 2,
18 error.Three => 3,
19 };
20}
21
22export fn entry() usize {
23 return @sizeOf(@TypeOf(&f)) + @sizeOf(@TypeOf(&h));
24}
25
26// error
27// backend=stage2
28// target=native
29//
30// :8:27: error: switch must handle all possibilities
31// :8:27: note: unhandled error value: 'error.Four'
32// :15:17: error: switch must handle all possibilities
33// :15:17: note: unhandled error value: 'error.Four'
test/cases/compile_errors/switch_expression-multiple_else_prongs.zig+20
...@@ -5,8 +5,24 @@ fn f(x: u32) void {...@@ -5,8 +5,24 @@ fn f(x: u32) void {
5 else => true,5 else => true,
6 };6 };
7}7}
8fn g(x: error{Foo, Bar, Baz}!u32) void {
9 const value: bool = if (x) |_| true else |e| switch (e) {
10 error.Foo => false,
11 else => true,
12 else => true,
13 };
14}
15fn h(x: error{Foo, Bar, Baz}!u32) void {
16 const value: u32 = x catch |e| switch (e) {
17 error.Foo => 1,
18 else => 2,
19 else => 3,
20 };
21}
8export fn entry() void {22export fn entry() void {
9 f(1234);23 f(1234);
24 g(1234);
25 h(1234);
10}26}
1127
12// error28// error
...@@ -15,3 +31,7 @@ export fn entry() void {...@@ -15,3 +31,7 @@ export fn entry() void {
15//31//
16// :5:9: error: multiple else prongs in switch expression32// :5:9: error: multiple else prongs in switch expression
17// :4:9: note: previous else prong here33// :4:9: note: previous else prong here
34// :12:9: error: multiple else prongs in switch expression
35// :11:9: note: previous else prong here
36// :19:9: error: multiple else prongs in switch expression
37// :18:9: note: previous else prong here
test/cases/compile_errors/switch_expression-unreachable_else_prong_error.zig created+32
...@@ -0,0 +1,32 @@
1fn foo(x: u2) void {
2 const y: Error!u2 = x;
3 if (y) |_| {} else |e| switch (e) {
4 error.Foo => {},
5 error.Bar => {},
6 error.Baz => {},
7 else => {},
8 }
9}
10
11fn bar(x: u2) void {
12 const y: Error!u2 = x;
13 y catch |e| switch (e) {
14 error.Foo => {},
15 error.Bar => {},
16 error.Baz => {},
17 else => {},
18 };
19}
20
21const Error = error{ Foo, Bar, Baz };
22
23export fn entry() usize {
24 return @sizeOf(@TypeOf(&foo)) + @sizeOf(@TypeOf(&bar));
25}
26
27// error
28// backend=stage2
29// target=native
30//
31// :7:14: error: unreachable else prong; all cases already handled
32// :17:14: error: unreachable else prong; all cases already handled
test/cases/compile_errors/switch_on_error_union_discard.zig created+12
...@@ -0,0 +1,12 @@
1export fn entry() void {
2 const x: error{}!u32 = 0;
3 if (x) |v| v else |_| switch (_) {
4 }
5}
6
7
8// error
9// backend=stage2
10// target=native
11//
12// :3:24: error: discard of error capture; omit it instead
test/cases/compile_errors/switch_on_error_with_1_field_with_no_prongs.zig created+20
...@@ -0,0 +1,20 @@
1const Error = error{M};
2
3export fn entry() void {
4 const f: Error!void = void{};
5 if (f) {} else |e| switch (e) {}
6}
7
8export fn entry2() void {
9 const f: Error!void = void{};
10 f catch |e| switch (e) {};
11}
12
13// error
14// backend=stage2
15// target=native
16//
17// :5:24: error: switch must handle all possibilities
18// :5:24: note: unhandled error value: 'error.M'
19// :10:17: error: switch must handle all possibilities
20// :10:17: note: unhandled error value: 'error.M'
test/cases/inherit_want_safety.zig+7
...@@ -23,6 +23,13 @@ pub export fn entry() usize {...@@ -23,6 +23,13 @@ pub export fn entry() usize {
23 u += 1;23 u += 1;
24 },24 },
25 }25 }
26 if (@as(error{}!usize, u)) |_| {
27 u += 1;
28 } else |e| switch (e) {
29 else => {
30 u += 1;
31 }
32 }
26 return u;33 return u;
27}34}
2835