| author | |
| committer | |
| log | acca16c8cee4529bcb11c150c2c99b7de32ce21f |
| tree | 78900c056830d99f6dfaa849faf23df8d4e53032 |
| parent | dbdee2d53cf22be8bcc9031c1c15a58ce530b131 |
| parent | 67d7d7b5a79cd709749f7435e1c3e4ef2e9150be |
| signature |
Special-case switching on error union capture14 files changed, 2412 insertions(+), 301 deletions(-)
doc/langref.html.in+26| ... | ... | @@ -6801,6 +6801,32 @@ test "peer type resolution: *const T and ?*T" { |
| 6801 | 6801 | try expect(a == b); |
| 6802 | 6802 | try expect(b == a); |
| 6803 | 6803 | } |
| 6804 | ||
| 6805 | test "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 | 6830 | {#code_end#} |
| 6805 | 6831 | {#header_close#} |
| 6806 | 6832 | {#header_close#} |
src/AstGen.zig+556-6| ... | ... | @@ -93,6 +93,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { |
| 93 | 93 | Zir.Inst.Call.Flags, |
| 94 | 94 | Zir.Inst.BuiltinCall.Flags, |
| 95 | 95 | Zir.Inst.SwitchBlock.Bits, |
| 96 | Zir.Inst.SwitchBlockErrUnion.Bits, | |
| 96 | 97 | Zir.Inst.FuncFancy.Bits, |
| 97 | 98 | => @bitCast(@field(extra, field.name)), |
| 98 | 99 | |
| ... | ... | @@ -838,7 +839,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 838 | 839 | |
| 839 | 840 | .if_simple, |
| 840 | 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 | }, | |
| 842 | 854 | |
| 843 | 855 | .while_simple, |
| 844 | 856 | .while_cont, |
| ... | ... | @@ -1014,10 +1026,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 1014 | 1026 | }, |
| 1015 | 1027 | .@"catch" => { |
| 1016 | 1028 | const catch_token = main_tokens[node]; |
| 1017 | const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) | |
| 1018 | catch_token + 2 | |
| 1019 | else | |
| 1020 | null; | |
| 1029 | const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) blk: { | |
| 1030 | if (token_tags.len > catch_token + 6 and | |
| 1031 | token_tags[catch_token + 4] == .keyword_switch) | |
| 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 | 1039 | switch (ri.rl) { |
| 1022 | 1040 | .ref, .ref_coerced_ty => return orelseCatchExpr( |
| 1023 | 1041 | gz, |
| ... | ... | @@ -2556,7 +2574,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2556 | 2574 | .vector_type, |
| 2557 | 2575 | .indexable_ptr_len, |
| 2558 | 2576 | .anyframe_type, |
| 2559 | .as, | |
| 2560 | 2577 | .as_node, |
| 2561 | 2578 | .as_shift_operand, |
| 2562 | 2579 | .bit_and, |
| ... | ... | @@ -2641,6 +2658,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2641 | 2658 | .import, |
| 2642 | 2659 | .switch_block, |
| 2643 | 2660 | .switch_block_ref, |
| 2661 | .switch_block_err_union, | |
| 2644 | 2662 | .union_init, |
| 2645 | 2663 | .field_type_ref, |
| 2646 | 2664 | .error_set_decl, |
| ... | ... | @@ -6858,6 +6876,538 @@ fn forExpr( |
| 6858 | 6876 | return result; |
| 6859 | 6877 | } |
| 6860 | 6878 | |
| 6879 | fn 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 | ||
| 6861 | 7411 | fn switchExpr( |
| 6862 | 7412 | parent_gz: *GenZir, |
| 6863 | 7413 | scope: *Scope, |
src/Sema.zig+718-287| ... | ... | @@ -1018,7 +1018,6 @@ fn analyzeBodyInner( |
| 1018 | 1018 | .array_type => try sema.zirArrayType(block, inst), |
| 1019 | 1019 | .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst), |
| 1020 | 1020 | .vector_type => try sema.zirVectorType(block, inst), |
| 1021 | .as => try sema.zirAs(block, inst), | |
| 1022 | 1021 | .as_node => try sema.zirAsNode(block, inst), |
| 1023 | 1022 | .as_shift_operand => try sema.zirAsShiftOperand(block, inst), |
| 1024 | 1023 | .bit_and => try sema.zirBitwise(block, inst, .bit_and), |
| ... | ... | @@ -1098,6 +1097,7 @@ fn analyzeBodyInner( |
| 1098 | 1097 | .str => try sema.zirStr(inst), |
| 1099 | 1098 | .switch_block => try sema.zirSwitchBlock(block, inst, false), |
| 1100 | 1099 | .switch_block_ref => try sema.zirSwitchBlock(block, inst, true), |
| 1100 | .switch_block_err_union => try sema.zirSwitchBlockErrUnion(block, inst), | |
| 1101 | 1101 | .type_info => try sema.zirTypeInfo(block, inst), |
| 1102 | 1102 | .size_of => try sema.zirSizeOf(block, inst), |
| 1103 | 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 | 8939 | const tracy = trace(@src()); |
| 8940 | 8940 | defer tracy.end(); |
| 8941 | 8941 | |
| 8942 | const mod = sema.mod; | |
| 8943 | 8942 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; |
| 8944 | 8943 | const src = inst_data.src(); |
| 8945 | 8944 | const operand = try sema.resolveInst(inst_data.operand); |
| 8945 | return sema.analyzeErrUnionCodePtr(block, src, operand); | |
| 8946 | } | |
| 8947 | ||
| 8948 | fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { | |
| 8949 | const mod = sema.mod; | |
| 8946 | 8950 | const operand_ty = sema.typeOf(operand); |
| 8947 | 8951 | assert(operand_ty.zigTypeTag(mod) == .Pointer); |
| 8948 | 8952 | |
| ... | ... | @@ -8957,7 +8961,10 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 8957 | 8961 | if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { |
| 8958 | 8962 | if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { |
| 8959 | 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 | } |
| 8963 | 8970 | |
| ... | ... | @@ -9794,14 +9801,6 @@ fn zirParamAnytype( |
| 9794 | 9801 | sema.inst_map.putAssumeCapacity(inst, .generic_poison); |
| 9795 | 9802 | } |
| 9796 | 9803 | |
| 9797 | fn 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 | ||
| 9805 | 9804 | fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9806 | 9805 | const tracy = trace(@src()); |
| 9807 | 9806 | defer tracy.end(); |
| ... | ... | @@ -11168,13 +11167,312 @@ fn switchCond( |
| 11168 | 11167 | |
| 11169 | 11168 | const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, Module.SwitchProngSrc); |
| 11170 | 11169 | |
| 11170 | fn 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 | ||
| 11171 | 11470 | fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref { |
| 11172 | 11471 | const tracy = trace(@src()); |
| 11173 | 11472 | defer tracy.end(); |
| 11174 | 11473 | |
| 11175 | 11474 | const mod = sema.mod; |
| 11176 | 11475 | const gpa = sema.gpa; |
| 11177 | const ip = &mod.intern_pool; | |
| 11178 | 11476 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; |
| 11179 | 11477 | const src = inst_data.src(); |
| 11180 | 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 | 11518 | var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); |
| 11221 | 11519 | defer case_vals.deinit(gpa); |
| 11222 | 11520 | |
| 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 | 11521 | const special_prong = extra.data.bits.specialProng(); |
| 11232 | const special: Special = switch (special_prong) { | |
| 11522 | const special: SpecialProng = switch (special_prong) { | |
| 11233 | 11523 | .none => .{ |
| 11234 | 11524 | .body = &.{}, |
| 11235 | 11525 | .end = header_extra_index, |
| ... | ... | @@ -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 => { | |
| 11413 | var extra_index: usize = special.end; | |
| 11414 | { | |
| 11415 | var scalar_i: u32 = 0; | |
| 11416 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | |
| 11417 | const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); | |
| 11418 | extra_index += 1; | |
| 11419 | const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); | |
| 11420 | extra_index += 1 + info.body_len; | |
| 11421 | ||
| 11422 | case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( | |
| 11423 | block, | |
| 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 | }, | |
| 11702 | .ErrorSet => else_error_ty = try validateErrSetSwitch( | |
| 11703 | sema, | |
| 11704 | block, | |
| 11705 | &seen_errors, | |
| 11706 | &case_vals, | |
| 11707 | operand_ty, | |
| 11708 | inst_data, | |
| 11709 | scalar_cases_len, | |
| 11710 | multi_cases_len, | |
| 11711 | .{ .body = special.body, .end = special.end, .src = special_prong_src }, | |
| 11712 | special_prong == .@"else", | |
| 11713 | ), | |
| 11556 | 11714 | .Int, .ComptimeInt => { |
| 11557 | 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 | 12006 | defer merges.deinit(gpa); |
| 11849 | 12007 | |
| 11850 | 12008 | if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| { |
| 11851 | const resolved_operand_val = try sema.resolveLazyValue(operand_val); | |
| 11852 | var extra_index: usize = special.end; | |
| 11853 | { | |
| 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( | |
| 12009 | return resolveSwitchComptime( | |
| 12010 | sema, | |
| 12011 | spa, | |
| 11950 | 12012 | &child_block, |
| 11951 | .special, | |
| 11952 | special.body, | |
| 11953 | special.capture, | |
| 11954 | .special_capture, | |
| 11955 | undefined, // case_vals may be undefined for special prongs | |
| 11956 | if (special.is_inline) operand else .none, | |
| 11957 | special.has_tag_capture, | |
| 11958 | merges, | |
| 12013 | operand, | |
| 12014 | operand_val, | |
| 12015 | operand_ty, | |
| 12016 | special, | |
| 12017 | case_vals, | |
| 12018 | scalar_cases_len, | |
| 12019 | multi_cases_len, | |
| 12020 | err_set, | |
| 12021 | empty_enum, | |
| 11959 | 12022 | ); |
| 11960 | 12023 | } |
| 11961 | 12024 | |
| ... | ... | @@ -11966,7 +12029,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11966 | 12029 | if (special_prong == .none) { |
| 11967 | 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 | 12033 | return .unreachable_value; |
| 11971 | 12034 | } |
| 11972 | 12035 | 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 | 12061 | unreachable; |
| 11999 | 12062 | } |
| 12000 | 12063 | |
| 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 | ||
| 12092 | const 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 | ||
| 12100 | fn 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 | 12131 | const estimated_cases_extra = (scalar_cases_len + multi_cases_len) * |
| 12002 | 12132 | @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2; |
| 12003 | 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 | 12162 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; |
| 12033 | 12163 | } else true; |
| 12034 | 12164 | |
| 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 | 12166 | // nothing to do here |
| 12037 | 12167 | } else if (analyze_body) { |
| 12038 | 12168 | try spa.analyzeProngRuntime( |
| ... | ... | @@ -12216,7 +12346,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12216 | 12346 | |
| 12217 | 12347 | const body = sema.code.bodySlice(extra_index, info.body_len); |
| 12218 | 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 | 12350 | // nothing to do here |
| 12221 | 12351 | } else if (analyze_body) { |
| 12222 | 12352 | try spa.analyzeProngRuntime( |
| ... | ... | @@ -12300,7 +12430,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12300 | 12430 | |
| 12301 | 12431 | const body = sema.code.bodySlice(extra_index, info.body_len); |
| 12302 | 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 | 12434 | // nothing to do here |
| 12305 | 12435 | } else { |
| 12306 | 12436 | try spa.analyzeProngRuntime( |
| ... | ... | @@ -12543,7 +12673,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12543 | 12673 | else |
| 12544 | 12674 | true; |
| 12545 | 12675 | 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 | 12678 | // nothing to do here |
| 12549 | 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 | 12718 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len + |
| 12589 | 12719 | cases_extra.items.len + final_else_body.len); |
| 12590 | 12720 | |
| 12591 | _ = try child_block.addInst(.{ .tag = .switch_br, .data = .{ .pl_op = .{ | |
| 12592 | .operand = operand, | |
| 12593 | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ | |
| 12594 | .cases_len = @intCast(cases_len), | |
| 12595 | .else_body_len = @intCast(final_else_body.len), | |
| 12596 | }), | |
| 12597 | } } }); | |
| 12721 | const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{ | |
| 12722 | .cases_len = @intCast(cases_len), | |
| 12723 | .else_body_len = @intCast(final_else_body.len), | |
| 12724 | }); | |
| 12725 | ||
| 12598 | 12726 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items)); |
| 12599 | 12727 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(final_else_body)); |
| 12600 | 12728 | |
| 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 | ||
| 12738 | fn 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 | } |
| 12603 | 12863 | |
| 12604 | 12864 | const RangeSetUnhandledIterator = struct { |
| ... | ... | @@ -12718,6 +12978,168 @@ fn resolveSwitchItemVal( |
| 12718 | 12978 | return .{ .ref = new_item, .val = val.toIntern() }; |
| 12719 | 12979 | } |
| 12720 | 12980 | |
| 12981 | fn 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 | ||
| 12721 | 13143 | fn validateSwitchRange( |
| 12722 | 13144 | sema: *Sema, |
| 12723 | 13145 | block: *Block, |
| ... | ... | @@ -12905,7 +13327,14 @@ fn validateSwitchNoRange( |
| 12905 | 13327 | return sema.failWithOwnedErrorMsg(block, msg); |
| 12906 | 13328 | } |
| 12907 | 13329 | |
| 12908 | fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref, operand_src: LazySrcLoc) !bool { | |
| 13330 | fn 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 | 13338 | const mod = sema.mod; |
| 12910 | 13339 | if (!mod.backendSupportsFeature(.panic_unwrap_error)) return false; |
| 12911 | 13340 | |
| ... | ... | @@ -12913,6 +13342,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op |
| 12913 | 13342 | for (body) |inst| { |
| 12914 | 13343 | switch (tags[@intFromEnum(inst)]) { |
| 12915 | 13344 | .@"unreachable" => if (!block.wantSafety()) return false, |
| 13345 | .err_union_code => if (!allow_err_code_inst) return false, | |
| 12916 | 13346 | .save_err_ret_index, |
| 12917 | 13347 | .dbg_block_begin, |
| 12918 | 13348 | .dbg_block_end, |
| ... | ... | @@ -12930,6 +13360,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op |
| 12930 | 13360 | const air_inst = switch (tags[@intFromEnum(inst)]) { |
| 12931 | 13361 | .dbg_block_begin, |
| 12932 | 13362 | .dbg_block_end, |
| 13363 | .err_union_code, | |
| 12933 | 13364 | => continue, |
| 12934 | 13365 | .dbg_stmt => { |
| 12935 | 13366 | try sema.zirDbgStmt(block, inst); |
| ... | ... | @@ -18393,7 +18824,7 @@ fn zirCondbr( |
| 18393 | 18824 | break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand); |
| 18394 | 18825 | }; |
| 18395 | 18826 | |
| 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 | 18828 | // nothing to do |
| 18398 | 18829 | } else { |
| 18399 | 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 | 100 | Inst.Call.Flags, |
| 101 | 101 | Inst.BuiltinCall.Flags, |
| 102 | 102 | Inst.SwitchBlock.Bits, |
| 103 | Inst.SwitchBlockErrUnion.Bits, | |
| 103 | 104 | Inst.FuncFancy.Bits, |
| 104 | 105 | => @bitCast(code.extra[i]), |
| 105 | 106 | |
| ... | ... | @@ -277,9 +278,6 @@ pub const Inst = struct { |
| 277 | 278 | /// Create a `anyframe->T` type. |
| 278 | 279 | /// Uses the `un_node` field. |
| 279 | 280 | anyframe_type, |
| 280 | /// Type coercion. No source location attached. | |
| 281 | /// Uses the `bin` field. | |
| 282 | as, | |
| 283 | 281 | /// Type coercion to the function's return type. |
| 284 | 282 | /// Uses the `pl_node` field. Payload is `As`. AST node could be many things. |
| 285 | 283 | as_node, |
| ... | ... | @@ -688,6 +686,9 @@ pub const Inst = struct { |
| 688 | 686 | /// A switch expression. Uses the `pl_node` union field. |
| 689 | 687 | /// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer. |
| 690 | 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 | 692 | /// Check that operand type supports the dereference operand (.*). |
| 692 | 693 | /// Uses the `un_node` field. |
| 693 | 694 | validate_deref, |
| ... | ... | @@ -1083,7 +1084,6 @@ pub const Inst = struct { |
| 1083 | 1084 | .vector_elem_type, |
| 1084 | 1085 | .indexable_ptr_len, |
| 1085 | 1086 | .anyframe_type, |
| 1086 | .as, | |
| 1087 | 1087 | .as_node, |
| 1088 | 1088 | .as_shift_operand, |
| 1089 | 1089 | .bit_and, |
| ... | ... | @@ -1190,6 +1190,7 @@ pub const Inst = struct { |
| 1190 | 1190 | .set_eval_branch_quota, |
| 1191 | 1191 | .switch_block, |
| 1192 | 1192 | .switch_block_ref, |
| 1193 | .switch_block_err_union, | |
| 1193 | 1194 | .validate_deref, |
| 1194 | 1195 | .validate_destructure, |
| 1195 | 1196 | .union_init, |
| ... | ... | @@ -1396,7 +1397,6 @@ pub const Inst = struct { |
| 1396 | 1397 | .vector_elem_type, |
| 1397 | 1398 | .indexable_ptr_len, |
| 1398 | 1399 | .anyframe_type, |
| 1399 | .as, | |
| 1400 | 1400 | .as_node, |
| 1401 | 1401 | .as_shift_operand, |
| 1402 | 1402 | .bit_and, |
| ... | ... | @@ -1488,6 +1488,7 @@ pub const Inst = struct { |
| 1488 | 1488 | .typeof_log2_int_type, |
| 1489 | 1489 | .switch_block, |
| 1490 | 1490 | .switch_block_ref, |
| 1491 | .switch_block_err_union, | |
| 1491 | 1492 | .union_init, |
| 1492 | 1493 | .field_type_ref, |
| 1493 | 1494 | .enum_from_int, |
| ... | ... | @@ -1629,7 +1630,6 @@ pub const Inst = struct { |
| 1629 | 1630 | .vector_elem_type = .un_node, |
| 1630 | 1631 | .indexable_ptr_len = .un_node, |
| 1631 | 1632 | .anyframe_type = .un_node, |
| 1632 | .as = .bin, | |
| 1633 | 1633 | .as_node = .pl_node, |
| 1634 | 1634 | .as_shift_operand = .pl_node, |
| 1635 | 1635 | .bit_and = .pl_node, |
| ... | ... | @@ -1741,6 +1741,7 @@ pub const Inst = struct { |
| 1741 | 1741 | .enum_literal = .str_tok, |
| 1742 | 1742 | .switch_block = .pl_node, |
| 1743 | 1743 | .switch_block_ref = .pl_node, |
| 1744 | .switch_block_err_union = .pl_node, | |
| 1744 | 1745 | .validate_deref = .un_node, |
| 1745 | 1746 | .validate_destructure = .pl_node, |
| 1746 | 1747 | .field_type_ref = .pl_node, |
| ... | ... | @@ -2782,6 +2783,29 @@ pub const Inst = struct { |
| 2782 | 2783 | index: u32, |
| 2783 | 2784 | }; |
| 2784 | 2785 | |
| 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 | 2809 | /// 0. multi_cases_len: u32 // If has_multi_cases is set. |
| 2786 | 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 | 2811 | /// 2. else_body { // If has_else or has_under is set. |
| ... | ... | @@ -2830,7 +2854,7 @@ pub const Inst = struct { |
| 2830 | 2854 | }; |
| 2831 | 2855 | }; |
| 2832 | 2856 | |
| 2833 | pub const Bits = packed struct { | |
| 2857 | pub const Bits = packed struct(u32) { | |
| 2834 | 2858 | /// If true, one or more prongs have multiple items. |
| 2835 | 2859 | has_multi_cases: bool, |
| 2836 | 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 | 199 | const tag = tags[@intFromEnum(inst)]; |
| 200 | 200 | try stream.print("= {s}(", .{@tagName(tags[@intFromEnum(inst)])}); |
| 201 | 201 | switch (tag) { |
| 202 | .as, | |
| 203 | 202 | .store, |
| 204 | 203 | .store_to_inferred_ptr, |
| 205 | 204 | => try self.writeBin(stream, inst), |
| ... | ... | @@ -465,6 +464,8 @@ const Writer = struct { |
| 465 | 464 | .switch_block_ref, |
| 466 | 465 | => try self.writeSwitchBlock(stream, inst), |
| 467 | 466 | |
| 467 | .switch_block_err_union => try self.writeSwitchBlockErrUnion(stream, inst), | |
| 468 | ||
| 468 | 469 | .field_val, |
| 469 | 470 | .field_ptr, |
| 470 | 471 | => try self.writePlNodeField(stream, inst), |
| ... | ... | @@ -2027,6 +2028,143 @@ const Writer = struct { |
| 2027 | 2028 | try self.writeSrc(stream, inst_data.src()); |
| 2028 | 2029 | } |
| 2029 | 2030 | |
| 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 | 2168 | fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| 2031 | 2169 | const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; |
| 2032 | 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const expect = std.testing.expect; | |
| 4 | const expectError = std.testing.expectError; | |
| 5 | const expectEqual = std.testing.expectEqual; | |
| 6 | ||
| 7 | test "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 | ||
| 259 | test "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 @@ |
| 1 | fn 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 | } | |
| 11 | fn 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 | ||
| 20 | const Error = error{ Foo, Bar, Baz }; | |
| 21 | ||
| 22 | export 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 @@ |
| 1 | fn 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 | } | |
| 12 | fn 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 | ||
| 22 | const Error = error{ Foo, Bar, Baz }; | |
| 23 | ||
| 24 | export 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 @@ |
| 1 | const Error = error { | |
| 2 | One, | |
| 3 | Two, | |
| 4 | Three, | |
| 5 | Four, | |
| 6 | }; | |
| 7 | fn 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 | } | |
| 14 | fn 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 | ||
| 22 | export 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 | 5 | else => true, |
| 6 | 6 | }; |
| 7 | 7 | } |
| 8 | fn 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 | } | |
| 15 | fn 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 | } | |
| 8 | 22 | export fn entry() void { |
| 9 | 23 | f(1234); |
| 24 | g(1234); | |
| 25 | h(1234); | |
| 10 | 26 | } |
| 11 | 27 | |
| 12 | 28 | // error |
| ... | ... | @@ -15,3 +31,7 @@ export fn entry() void { |
| 15 | 31 | // |
| 16 | 32 | // :5:9: error: multiple else prongs in switch expression |
| 17 | 33 | // :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 @@ |
| 1 | fn 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 | ||
| 11 | fn 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 | ||
| 21 | const Error = error{ Foo, Bar, Baz }; | |
| 22 | ||
| 23 | export 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 @@ |
| 1 | export 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 @@ |
| 1 | const Error = error{M}; | |
| 2 | ||
| 3 | export fn entry() void { | |
| 4 | const f: Error!void = void{}; | |
| 5 | if (f) {} else |e| switch (e) {} | |
| 6 | } | |
| 7 | ||
| 8 | export 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 | 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 | 33 | return u; |
| 27 | 34 | } |
| 28 | 35 |