| 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,6 +6801,32 @@ test "peer type resolution: *const T and ?*T" { |
| 6801 | try expect(a == b); | 6801 | try expect(a == b); |
| 6802 | try expect(b == a); | 6802 | try expect(b == a); |
| 6803 | } | 6803 | } |
| 6804 | |||
| 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 | {#code_end#} | 6830 | {#code_end#} |
| 6805 | {#header_close#} | 6831 | {#header_close#} |
| 6806 | {#header_close#} | 6832 | {#header_close#} |
src/AstGen.zig+556-6| ... | @@ -93,6 +93,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { | ... | @@ -93,6 +93,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { |
| 93 | Zir.Inst.Call.Flags, | 93 | Zir.Inst.Call.Flags, |
| 94 | Zir.Inst.BuiltinCall.Flags, | 94 | Zir.Inst.BuiltinCall.Flags, |
| 95 | Zir.Inst.SwitchBlock.Bits, | 95 | Zir.Inst.SwitchBlock.Bits, |
| 96 | Zir.Inst.SwitchBlockErrUnion.Bits, | ||
| 96 | Zir.Inst.FuncFancy.Bits, | 97 | Zir.Inst.FuncFancy.Bits, |
| 97 | => @bitCast(@field(extra, field.name)), | 98 | => @bitCast(@field(extra, field.name)), |
| 98 | 99 | ||
| ... | @@ -838,7 +839,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE | ... | @@ -838,7 +839,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 838 | 839 | ||
| 839 | .if_simple, | 840 | .if_simple, |
| 840 | .@"if", | 841 | .@"if", |
| 841 | => return ifExpr(gz, scope, ri.br(), node, tree.fullIf(node).?), | 842 | => { |
| 843 | const if_full = tree.fullIf(node).?; | ||
| 844 | if (if_full.error_token) |error_token| { | ||
| 845 | const tag = node_tags[if_full.ast.else_expr]; | ||
| 846 | if ((tag == .@"switch" or tag == .switch_comma) and | ||
| 847 | std.mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(error_token + 4))) | ||
| 848 | { | ||
| 849 | return switchExprErrUnion(gz, scope, ri.br(), node, .@"if"); | ||
| 850 | } | ||
| 851 | } | ||
| 852 | return ifExpr(gz, scope, ri.br(), node, if_full); | ||
| 853 | }, | ||
| 842 | 854 | ||
| 843 | .while_simple, | 855 | .while_simple, |
| 844 | .while_cont, | 856 | .while_cont, |
| ... | @@ -1014,10 +1026,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE | ... | @@ -1014,10 +1026,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 1014 | }, | 1026 | }, |
| 1015 | .@"catch" => { | 1027 | .@"catch" => { |
| 1016 | const catch_token = main_tokens[node]; | 1028 | const catch_token = main_tokens[node]; |
| 1017 | const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) | 1029 | const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe) blk: { |
| 1018 | catch_token + 2 | 1030 | if (token_tags.len > catch_token + 6 and |
| 1019 | else | 1031 | token_tags[catch_token + 4] == .keyword_switch) |
| 1020 | null; | 1032 | { |
| 1033 | if (std.mem.eql(u8, tree.tokenSlice(catch_token + 2), tree.tokenSlice(catch_token + 6))) { | ||
| 1034 | return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch"); | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | break :blk catch_token + 2; | ||
| 1038 | } else null; | ||
| 1021 | switch (ri.rl) { | 1039 | switch (ri.rl) { |
| 1022 | .ref, .ref_coerced_ty => return orelseCatchExpr( | 1040 | .ref, .ref_coerced_ty => return orelseCatchExpr( |
| 1023 | gz, | 1041 | gz, |
| ... | @@ -2556,7 +2574,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2556,7 +2574,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2556 | .vector_type, | 2574 | .vector_type, |
| 2557 | .indexable_ptr_len, | 2575 | .indexable_ptr_len, |
| 2558 | .anyframe_type, | 2576 | .anyframe_type, |
| 2559 | .as, | ||
| 2560 | .as_node, | 2577 | .as_node, |
| 2561 | .as_shift_operand, | 2578 | .as_shift_operand, |
| 2562 | .bit_and, | 2579 | .bit_and, |
| ... | @@ -2641,6 +2658,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2641,6 +2658,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2641 | .import, | 2658 | .import, |
| 2642 | .switch_block, | 2659 | .switch_block, |
| 2643 | .switch_block_ref, | 2660 | .switch_block_ref, |
| 2661 | .switch_block_err_union, | ||
| 2644 | .union_init, | 2662 | .union_init, |
| 2645 | .field_type_ref, | 2663 | .field_type_ref, |
| 2646 | .error_set_decl, | 2664 | .error_set_decl, |
| ... | @@ -6858,6 +6876,538 @@ fn forExpr( | ... | @@ -6858,6 +6876,538 @@ fn forExpr( |
| 6858 | return result; | 6876 | return result; |
| 6859 | } | 6877 | } |
| 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 | fn switchExpr( | 7411 | fn switchExpr( |
| 6862 | parent_gz: *GenZir, | 7412 | parent_gz: *GenZir, |
| 6863 | scope: *Scope, | 7413 | scope: *Scope, |
src/Sema.zig+718-287| ... | @@ -1018,7 +1018,6 @@ fn analyzeBodyInner( | ... | @@ -1018,7 +1018,6 @@ fn analyzeBodyInner( |
| 1018 | .array_type => try sema.zirArrayType(block, inst), | 1018 | .array_type => try sema.zirArrayType(block, inst), |
| 1019 | .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst), | 1019 | .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst), |
| 1020 | .vector_type => try sema.zirVectorType(block, inst), | 1020 | .vector_type => try sema.zirVectorType(block, inst), |
| 1021 | .as => try sema.zirAs(block, inst), | ||
| 1022 | .as_node => try sema.zirAsNode(block, inst), | 1021 | .as_node => try sema.zirAsNode(block, inst), |
| 1023 | .as_shift_operand => try sema.zirAsShiftOperand(block, inst), | 1022 | .as_shift_operand => try sema.zirAsShiftOperand(block, inst), |
| 1024 | .bit_and => try sema.zirBitwise(block, inst, .bit_and), | 1023 | .bit_and => try sema.zirBitwise(block, inst, .bit_and), |
| ... | @@ -1098,6 +1097,7 @@ fn analyzeBodyInner( | ... | @@ -1098,6 +1097,7 @@ fn analyzeBodyInner( |
| 1098 | .str => try sema.zirStr(inst), | 1097 | .str => try sema.zirStr(inst), |
| 1099 | .switch_block => try sema.zirSwitchBlock(block, inst, false), | 1098 | .switch_block => try sema.zirSwitchBlock(block, inst, false), |
| 1100 | .switch_block_ref => try sema.zirSwitchBlock(block, inst, true), | 1099 | .switch_block_ref => try sema.zirSwitchBlock(block, inst, true), |
| 1100 | .switch_block_err_union => try sema.zirSwitchBlockErrUnion(block, inst), | ||
| 1101 | .type_info => try sema.zirTypeInfo(block, inst), | 1101 | .type_info => try sema.zirTypeInfo(block, inst), |
| 1102 | .size_of => try sema.zirSizeOf(block, inst), | 1102 | .size_of => try sema.zirSizeOf(block, inst), |
| 1103 | .bit_size_of => try sema.zirBitSizeOf(block, inst), | 1103 | .bit_size_of => try sema.zirBitSizeOf(block, inst), |
| ... | @@ -8939,10 +8939,14 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE | ... | @@ -8939,10 +8939,14 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 8939 | const tracy = trace(@src()); | 8939 | const tracy = trace(@src()); |
| 8940 | defer tracy.end(); | 8940 | defer tracy.end(); |
| 8941 | 8941 | ||
| 8942 | const mod = sema.mod; | ||
| 8943 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; | 8942 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; |
| 8944 | const src = inst_data.src(); | 8943 | const src = inst_data.src(); |
| 8945 | const operand = try sema.resolveInst(inst_data.operand); | 8944 | const operand = try sema.resolveInst(inst_data.operand); |
| 8945 | return sema.analyzeErrUnionCodePtr(block, src, operand); | ||
| 8946 | } | ||
| 8947 | |||
| 8948 | fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { | ||
| 8949 | const mod = sema.mod; | ||
| 8946 | const operand_ty = sema.typeOf(operand); | 8950 | const operand_ty = sema.typeOf(operand); |
| 8947 | assert(operand_ty.zigTypeTag(mod) == .Pointer); | 8951 | assert(operand_ty.zigTypeTag(mod) == .Pointer); |
| 8948 | 8952 | ||
| ... | @@ -8957,7 +8961,10 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE | ... | @@ -8957,7 +8961,10 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 8957 | if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { | 8961 | if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { |
| 8958 | if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { | 8962 | if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { |
| 8959 | assert(val.getErrorName(mod) != .none); | 8963 | assert(val.getErrorName(mod) != .none); |
| 8960 | return Air.internedToRef(val.toIntern()); | 8964 | return Air.internedToRef((try mod.intern(.{ .err = .{ |
| 8965 | .ty = result_ty.toIntern(), | ||
| 8966 | .name = mod.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name, | ||
| 8967 | } }))); | ||
| 8961 | } | 8968 | } |
| 8962 | } | 8969 | } |
| 8963 | 8970 | ||
| ... | @@ -9794,14 +9801,6 @@ fn zirParamAnytype( | ... | @@ -9794,14 +9801,6 @@ fn zirParamAnytype( |
| 9794 | sema.inst_map.putAssumeCapacity(inst, .generic_poison); | 9801 | sema.inst_map.putAssumeCapacity(inst, .generic_poison); |
| 9795 | } | 9802 | } |
| 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 | fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 9804 | fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9806 | const tracy = trace(@src()); | 9805 | const tracy = trace(@src()); |
| 9807 | defer tracy.end(); | 9806 | defer tracy.end(); |
| ... | @@ -11168,13 +11167,312 @@ fn switchCond( | ... | @@ -11168,13 +11167,312 @@ fn switchCond( |
| 11168 | 11167 | ||
| 11169 | const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, Module.SwitchProngSrc); | 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 | fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref { | 11470 | fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref { |
| 11172 | const tracy = trace(@src()); | 11471 | const tracy = trace(@src()); |
| 11173 | defer tracy.end(); | 11472 | defer tracy.end(); |
| 11174 | 11473 | ||
| 11175 | const mod = sema.mod; | 11474 | const mod = sema.mod; |
| 11176 | const gpa = sema.gpa; | 11475 | const gpa = sema.gpa; |
| 11177 | const ip = &mod.intern_pool; | ||
| 11178 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; | 11476 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; |
| 11179 | const src = inst_data.src(); | 11477 | const src = inst_data.src(); |
| 11180 | const src_node_offset = inst_data.src_node; | 11478 | const src_node_offset = inst_data.src_node; |
| ... | @@ -11220,16 +11518,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11220,16 +11518,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11220 | var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); | 11518 | var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len); |
| 11221 | defer case_vals.deinit(gpa); | 11519 | defer case_vals.deinit(gpa); |
| 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 | const special_prong = extra.data.bits.specialProng(); | 11521 | const special_prong = extra.data.bits.specialProng(); |
| 11232 | const special: Special = switch (special_prong) { | 11522 | const special: SpecialProng = switch (special_prong) { |
| 11233 | .none => .{ | 11523 | .none => .{ |
| 11234 | .body = &.{}, | 11524 | .body = &.{}, |
| 11235 | .end = header_extra_index, | 11525 | .end = header_extra_index, |
| ... | @@ -11409,150 +11699,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11409,150 +11699,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11409 | ); | 11699 | ); |
| 11410 | } | 11700 | } |
| 11411 | }, | 11701 | }, |
| 11412 | .ErrorSet => { | 11702 | .ErrorSet => else_error_ty = try validateErrSetSwitch( |
| 11413 | var extra_index: usize = special.end; | 11703 | sema, |
| 11414 | { | 11704 | block, |
| 11415 | var scalar_i: u32 = 0; | 11705 | &seen_errors, |
| 11416 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | 11706 | &case_vals, |
| 11417 | const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); | 11707 | operand_ty, |
| 11418 | extra_index += 1; | 11708 | inst_data, |
| 11419 | const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); | 11709 | scalar_cases_len, |
| 11420 | extra_index += 1 + info.body_len; | 11710 | multi_cases_len, |
| 11421 | 11711 | .{ .body = special.body, .end = special.end, .src = special_prong_src }, | |
| 11422 | case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( | 11712 | special_prong == .@"else", |
| 11423 | block, | 11713 | ), |
| 11424 | &seen_errors, | ||
| 11425 | item_ref, | ||
| 11426 | operand_ty, | ||
| 11427 | src_node_offset, | ||
| 11428 | .{ .scalar = scalar_i }, | ||
| 11429 | )); | ||
| 11430 | } | ||
| 11431 | } | ||
| 11432 | { | ||
| 11433 | var multi_i: u32 = 0; | ||
| 11434 | while (multi_i < multi_cases_len) : (multi_i += 1) { | ||
| 11435 | const items_len = sema.code.extra[extra_index]; | ||
| 11436 | extra_index += 1; | ||
| 11437 | const ranges_len = sema.code.extra[extra_index]; | ||
| 11438 | extra_index += 1; | ||
| 11439 | const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); | ||
| 11440 | extra_index += 1; | ||
| 11441 | const items = sema.code.refSlice(extra_index, items_len); | ||
| 11442 | extra_index += items_len + info.body_len; | ||
| 11443 | |||
| 11444 | try case_vals.ensureUnusedCapacity(gpa, items.len); | ||
| 11445 | for (items, 0..) |item_ref, item_i| { | ||
| 11446 | case_vals.appendAssumeCapacity(try sema.validateSwitchItemError( | ||
| 11447 | block, | ||
| 11448 | &seen_errors, | ||
| 11449 | item_ref, | ||
| 11450 | operand_ty, | ||
| 11451 | src_node_offset, | ||
| 11452 | .{ .multi = .{ .prong = multi_i, .item = @intCast(item_i) } }, | ||
| 11453 | )); | ||
| 11454 | } | ||
| 11455 | |||
| 11456 | try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset); | ||
| 11457 | } | ||
| 11458 | } | ||
| 11459 | |||
| 11460 | switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) { | ||
| 11461 | .anyerror_type => { | ||
| 11462 | if (special_prong != .@"else") { | ||
| 11463 | return sema.fail( | ||
| 11464 | block, | ||
| 11465 | src, | ||
| 11466 | "else prong required when switching on type 'anyerror'", | ||
| 11467 | .{}, | ||
| 11468 | ); | ||
| 11469 | } | ||
| 11470 | else_error_ty = Type.anyerror; | ||
| 11471 | }, | ||
| 11472 | else => |err_set_ty_index| else_validation: { | ||
| 11473 | const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names; | ||
| 11474 | var maybe_msg: ?*Module.ErrorMsg = null; | ||
| 11475 | errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa); | ||
| 11476 | |||
| 11477 | for (error_names.get(ip)) |error_name| { | ||
| 11478 | if (!seen_errors.contains(error_name) and special_prong != .@"else") { | ||
| 11479 | const msg = maybe_msg orelse blk: { | ||
| 11480 | maybe_msg = try sema.errMsg( | ||
| 11481 | block, | ||
| 11482 | src, | ||
| 11483 | "switch must handle all possibilities", | ||
| 11484 | .{}, | ||
| 11485 | ); | ||
| 11486 | break :blk maybe_msg.?; | ||
| 11487 | }; | ||
| 11488 | |||
| 11489 | try sema.errNote( | ||
| 11490 | block, | ||
| 11491 | src, | ||
| 11492 | msg, | ||
| 11493 | "unhandled error value: 'error.{}'", | ||
| 11494 | .{error_name.fmt(ip)}, | ||
| 11495 | ); | ||
| 11496 | } | ||
| 11497 | } | ||
| 11498 | |||
| 11499 | if (maybe_msg) |msg| { | ||
| 11500 | maybe_msg = null; | ||
| 11501 | try sema.addDeclaredHereNote(msg, operand_ty); | ||
| 11502 | return sema.failWithOwnedErrorMsg(block, msg); | ||
| 11503 | } | ||
| 11504 | |||
| 11505 | if (special_prong == .@"else" and | ||
| 11506 | seen_errors.count() == error_names.len) | ||
| 11507 | { | ||
| 11508 | // In order to enable common patterns for generic code allow simple else bodies | ||
| 11509 | // else => unreachable, | ||
| 11510 | // else => return, | ||
| 11511 | // else => |e| return e, | ||
| 11512 | // even if all the possible errors were already handled. | ||
| 11513 | const tags = sema.code.instructions.items(.tag); | ||
| 11514 | for (special.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) { | ||
| 11515 | .dbg_block_begin, | ||
| 11516 | .dbg_block_end, | ||
| 11517 | .dbg_stmt, | ||
| 11518 | .dbg_var_val, | ||
| 11519 | .ret_type, | ||
| 11520 | .as_node, | ||
| 11521 | .ret_node, | ||
| 11522 | .@"unreachable", | ||
| 11523 | .@"defer", | ||
| 11524 | .defer_err_code, | ||
| 11525 | .err_union_code, | ||
| 11526 | .ret_err_value_code, | ||
| 11527 | .restore_err_ret_index, | ||
| 11528 | .is_non_err, | ||
| 11529 | .ret_is_non_err, | ||
| 11530 | .condbr, | ||
| 11531 | => {}, | ||
| 11532 | else => break, | ||
| 11533 | } else break :else_validation; | ||
| 11534 | |||
| 11535 | return sema.fail( | ||
| 11536 | block, | ||
| 11537 | special_prong_src, | ||
| 11538 | "unreachable else prong; all cases already handled", | ||
| 11539 | .{}, | ||
| 11540 | ); | ||
| 11541 | } | ||
| 11542 | |||
| 11543 | var names: InferredErrorSet.NameMap = .{}; | ||
| 11544 | try names.ensureUnusedCapacity(sema.arena, error_names.len); | ||
| 11545 | for (error_names.get(ip)) |error_name| { | ||
| 11546 | if (seen_errors.contains(error_name)) continue; | ||
| 11547 | |||
| 11548 | names.putAssumeCapacityNoClobber(error_name, {}); | ||
| 11549 | } | ||
| 11550 | // No need to keep the hash map metadata correct; here we | ||
| 11551 | // extract the (sorted) keys only. | ||
| 11552 | else_error_ty = try mod.errorSetFromUnsortedNames(names.keys()); | ||
| 11553 | }, | ||
| 11554 | } | ||
| 11555 | }, | ||
| 11556 | .Int, .ComptimeInt => { | 11714 | .Int, .ComptimeInt => { |
| 11557 | var extra_index: usize = special.end; | 11715 | var extra_index: usize = special.end; |
| 11558 | { | 11716 | { |
| ... | @@ -11848,114 +12006,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11848,114 +12006,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11848 | defer merges.deinit(gpa); | 12006 | defer merges.deinit(gpa); |
| 11849 | 12007 | ||
| 11850 | if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| { | 12008 | if (try sema.resolveDefinedValue(&child_block, src, operand)) |operand_val| { |
| 11851 | const resolved_operand_val = try sema.resolveLazyValue(operand_val); | 12009 | return resolveSwitchComptime( |
| 11852 | var extra_index: usize = special.end; | 12010 | sema, |
| 11853 | { | 12011 | spa, |
| 11854 | var scalar_i: usize = 0; | ||
| 11855 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | ||
| 11856 | extra_index += 1; | ||
| 11857 | const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); | ||
| 11858 | extra_index += 1; | ||
| 11859 | const body = sema.code.bodySlice(extra_index, info.body_len); | ||
| 11860 | extra_index += info.body_len; | ||
| 11861 | |||
| 11862 | const item = case_vals.items[scalar_i]; | ||
| 11863 | const item_val = sema.resolveConstDefinedValue(&child_block, .unneeded, item, undefined) catch unreachable; | ||
| 11864 | if (operand_val.eql(item_val, operand_ty, sema.mod)) { | ||
| 11865 | if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); | ||
| 11866 | return spa.resolveProngComptime( | ||
| 11867 | &child_block, | ||
| 11868 | .normal, | ||
| 11869 | body, | ||
| 11870 | info.capture, | ||
| 11871 | .{ .scalar_capture = @intCast(scalar_i) }, | ||
| 11872 | &.{item}, | ||
| 11873 | if (info.is_inline) operand else .none, | ||
| 11874 | info.has_tag_capture, | ||
| 11875 | merges, | ||
| 11876 | ); | ||
| 11877 | } | ||
| 11878 | } | ||
| 11879 | } | ||
| 11880 | { | ||
| 11881 | var multi_i: usize = 0; | ||
| 11882 | var case_val_idx: usize = scalar_cases_len; | ||
| 11883 | while (multi_i < multi_cases_len) : (multi_i += 1) { | ||
| 11884 | const items_len = sema.code.extra[extra_index]; | ||
| 11885 | extra_index += 1; | ||
| 11886 | const ranges_len = sema.code.extra[extra_index]; | ||
| 11887 | extra_index += 1; | ||
| 11888 | const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]); | ||
| 11889 | extra_index += 1 + items_len; | ||
| 11890 | const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len); | ||
| 11891 | |||
| 11892 | const items = case_vals.items[case_val_idx..][0..items_len]; | ||
| 11893 | case_val_idx += items_len; | ||
| 11894 | |||
| 11895 | for (items) |item| { | ||
| 11896 | // Validation above ensured these will succeed. | ||
| 11897 | const item_val = sema.resolveConstDefinedValue(&child_block, .unneeded, item, undefined) catch unreachable; | ||
| 11898 | if (operand_val.eql(item_val, operand_ty, sema.mod)) { | ||
| 11899 | if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); | ||
| 11900 | return spa.resolveProngComptime( | ||
| 11901 | &child_block, | ||
| 11902 | .normal, | ||
| 11903 | body, | ||
| 11904 | info.capture, | ||
| 11905 | .{ .multi_capture = @intCast(multi_i) }, | ||
| 11906 | items, | ||
| 11907 | if (info.is_inline) operand else .none, | ||
| 11908 | info.has_tag_capture, | ||
| 11909 | merges, | ||
| 11910 | ); | ||
| 11911 | } | ||
| 11912 | } | ||
| 11913 | |||
| 11914 | var range_i: usize = 0; | ||
| 11915 | while (range_i < ranges_len) : (range_i += 1) { | ||
| 11916 | const range_items = case_vals.items[case_val_idx..][0..2]; | ||
| 11917 | extra_index += 2; | ||
| 11918 | case_val_idx += 2; | ||
| 11919 | |||
| 11920 | // Validation above ensured these will succeed. | ||
| 11921 | const first_val = sema.resolveConstDefinedValue(&child_block, .unneeded, range_items[0], undefined) catch unreachable; | ||
| 11922 | const last_val = sema.resolveConstDefinedValue(&child_block, .unneeded, range_items[1], undefined) catch unreachable; | ||
| 11923 | if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and | ||
| 11924 | (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty))) | ||
| 11925 | { | ||
| 11926 | if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); | ||
| 11927 | return spa.resolveProngComptime( | ||
| 11928 | &child_block, | ||
| 11929 | .normal, | ||
| 11930 | body, | ||
| 11931 | info.capture, | ||
| 11932 | .{ .multi_capture = @intCast(multi_i) }, | ||
| 11933 | undefined, // case_vals may be undefined for ranges | ||
| 11934 | if (info.is_inline) operand else .none, | ||
| 11935 | info.has_tag_capture, | ||
| 11936 | merges, | ||
| 11937 | ); | ||
| 11938 | } | ||
| 11939 | } | ||
| 11940 | |||
| 11941 | extra_index += info.body_len; | ||
| 11942 | } | ||
| 11943 | } | ||
| 11944 | if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, special.body, operand); | ||
| 11945 | if (empty_enum) { | ||
| 11946 | return .void_value; | ||
| 11947 | } | ||
| 11948 | |||
| 11949 | return spa.resolveProngComptime( | ||
| 11950 | &child_block, | 12012 | &child_block, |
| 11951 | .special, | 12013 | operand, |
| 11952 | special.body, | 12014 | operand_val, |
| 11953 | special.capture, | 12015 | operand_ty, |
| 11954 | .special_capture, | 12016 | special, |
| 11955 | undefined, // case_vals may be undefined for special prongs | 12017 | case_vals, |
| 11956 | if (special.is_inline) operand else .none, | 12018 | scalar_cases_len, |
| 11957 | special.has_tag_capture, | 12019 | multi_cases_len, |
| 11958 | merges, | 12020 | err_set, |
| 12021 | empty_enum, | ||
| 11959 | ); | 12022 | ); |
| 11960 | } | 12023 | } |
| 11961 | 12024 | ||
| ... | @@ -11966,7 +12029,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -11966,7 +12029,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11966 | if (special_prong == .none) { | 12029 | if (special_prong == .none) { |
| 11967 | return sema.fail(block, src, "switch must handle all possibilities", .{}); | 12030 | return sema.fail(block, src, "switch must handle all possibilities", .{}); |
| 11968 | } | 12031 | } |
| 11969 | if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand, operand_src)) { | 12032 | if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand, operand_src, false)) { |
| 11970 | return .unreachable_value; | 12033 | return .unreachable_value; |
| 11971 | } | 12034 | } |
| 11972 | if (mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and | 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,6 +12061,73 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11998 | unreachable; | 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 | const estimated_cases_extra = (scalar_cases_len + multi_cases_len) * | 12131 | const estimated_cases_extra = (scalar_cases_len + multi_cases_len) * |
| 12002 | @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2; | 12132 | @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2; |
| 12003 | var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra); | 12133 | var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra); |
| ... | @@ -12032,7 +12162,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12032,7 +12162,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12032 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; | 12162 | break :blk field_ty.zigTypeTag(mod) != .NoReturn; |
| 12033 | } else true; | 12163 | } else true; |
| 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 | // nothing to do here | 12166 | // nothing to do here |
| 12037 | } else if (analyze_body) { | 12167 | } else if (analyze_body) { |
| 12038 | try spa.analyzeProngRuntime( | 12168 | try spa.analyzeProngRuntime( |
| ... | @@ -12216,7 +12346,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12216,7 +12346,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12216 | 12346 | ||
| 12217 | const body = sema.code.bodySlice(extra_index, info.body_len); | 12347 | const body = sema.code.bodySlice(extra_index, info.body_len); |
| 12218 | extra_index += info.body_len; | 12348 | extra_index += info.body_len; |
| 12219 | if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) { | 12349 | if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) { |
| 12220 | // nothing to do here | 12350 | // nothing to do here |
| 12221 | } else if (analyze_body) { | 12351 | } else if (analyze_body) { |
| 12222 | try spa.analyzeProngRuntime( | 12352 | try spa.analyzeProngRuntime( |
| ... | @@ -12300,7 +12430,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12300,7 +12430,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12300 | 12430 | ||
| 12301 | const body = sema.code.bodySlice(extra_index, info.body_len); | 12431 | const body = sema.code.bodySlice(extra_index, info.body_len); |
| 12302 | extra_index += info.body_len; | 12432 | extra_index += info.body_len; |
| 12303 | if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) { | 12433 | if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap)) { |
| 12304 | // nothing to do here | 12434 | // nothing to do here |
| 12305 | } else { | 12435 | } else { |
| 12306 | try spa.analyzeProngRuntime( | 12436 | try spa.analyzeProngRuntime( |
| ... | @@ -12543,7 +12673,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12543,7 +12673,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12543 | else | 12673 | else |
| 12544 | true; | 12674 | true; |
| 12545 | if (special.body.len != 0 and err_set and | 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 | // nothing to do here | 12678 | // nothing to do here |
| 12549 | } else if (special.body.len != 0 and analyze_body and !special.is_inline) { | 12679 | } else if (special.body.len != 0 and analyze_body and !special.is_inline) { |
| ... | @@ -12588,17 +12718,147 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r | ... | @@ -12588,17 +12718,147 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 12588 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len + | 12718 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).Struct.fields.len + |
| 12589 | cases_extra.items.len + final_else_body.len); | 12719 | cases_extra.items.len + final_else_body.len); |
| 12590 | 12720 | ||
| 12591 | _ = try child_block.addInst(.{ .tag = .switch_br, .data = .{ .pl_op = .{ | 12721 | const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 12592 | .operand = operand, | 12722 | .cases_len = @intCast(cases_len), |
| 12593 | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ | 12723 | .else_body_len = @intCast(final_else_body.len), |
| 12594 | .cases_len = @intCast(cases_len), | 12724 | }); |
| 12595 | .else_body_len = @intCast(final_else_body.len), | 12725 | |
| 12596 | }), | ||
| 12597 | } } }); | ||
| 12598 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items)); | 12726 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items)); |
| 12599 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(final_else_body)); | 12727 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(final_else_body)); |
| 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 | const RangeSetUnhandledIterator = struct { | 12864 | const RangeSetUnhandledIterator = struct { |
| ... | @@ -12718,6 +12978,168 @@ fn resolveSwitchItemVal( | ... | @@ -12718,6 +12978,168 @@ fn resolveSwitchItemVal( |
| 12718 | return .{ .ref = new_item, .val = val.toIntern() }; | 12978 | return .{ .ref = new_item, .val = val.toIntern() }; |
| 12719 | } | 12979 | } |
| 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 | fn validateSwitchRange( | 13143 | fn validateSwitchRange( |
| 12722 | sema: *Sema, | 13144 | sema: *Sema, |
| 12723 | block: *Block, | 13145 | block: *Block, |
| ... | @@ -12905,7 +13327,14 @@ fn validateSwitchNoRange( | ... | @@ -12905,7 +13327,14 @@ fn validateSwitchNoRange( |
| 12905 | return sema.failWithOwnedErrorMsg(block, msg); | 13327 | return sema.failWithOwnedErrorMsg(block, msg); |
| 12906 | } | 13328 | } |
| 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 | const mod = sema.mod; | 13338 | const mod = sema.mod; |
| 12910 | if (!mod.backendSupportsFeature(.panic_unwrap_error)) return false; | 13339 | if (!mod.backendSupportsFeature(.panic_unwrap_error)) return false; |
| 12911 | 13340 | ||
| ... | @@ -12913,6 +13342,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op | ... | @@ -12913,6 +13342,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op |
| 12913 | for (body) |inst| { | 13342 | for (body) |inst| { |
| 12914 | switch (tags[@intFromEnum(inst)]) { | 13343 | switch (tags[@intFromEnum(inst)]) { |
| 12915 | .@"unreachable" => if (!block.wantSafety()) return false, | 13344 | .@"unreachable" => if (!block.wantSafety()) return false, |
| 13345 | .err_union_code => if (!allow_err_code_inst) return false, | ||
| 12916 | .save_err_ret_index, | 13346 | .save_err_ret_index, |
| 12917 | .dbg_block_begin, | 13347 | .dbg_block_begin, |
| 12918 | .dbg_block_end, | 13348 | .dbg_block_end, |
| ... | @@ -12930,6 +13360,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op | ... | @@ -12930,6 +13360,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op |
| 12930 | const air_inst = switch (tags[@intFromEnum(inst)]) { | 13360 | const air_inst = switch (tags[@intFromEnum(inst)]) { |
| 12931 | .dbg_block_begin, | 13361 | .dbg_block_begin, |
| 12932 | .dbg_block_end, | 13362 | .dbg_block_end, |
| 13363 | .err_union_code, | ||
| 12933 | => continue, | 13364 | => continue, |
| 12934 | .dbg_stmt => { | 13365 | .dbg_stmt => { |
| 12935 | try sema.zirDbgStmt(block, inst); | 13366 | try sema.zirDbgStmt(block, inst); |
| ... | @@ -18393,7 +18824,7 @@ fn zirCondbr( | ... | @@ -18393,7 +18824,7 @@ fn zirCondbr( |
| 18393 | break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand); | 18824 | break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand); |
| 18394 | }; | 18825 | }; |
| 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 | // nothing to do | 18828 | // nothing to do |
| 18398 | } else { | 18829 | } else { |
| 18399 | try sema.analyzeBodyRuntimeBreak(&sub_block, else_body); | 18830 | try sema.analyzeBodyRuntimeBreak(&sub_block, else_body); |
src/Zir.zig+31-7| ... | @@ -100,6 +100,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) { | ... | @@ -100,6 +100,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) { |
| 100 | Inst.Call.Flags, | 100 | Inst.Call.Flags, |
| 101 | Inst.BuiltinCall.Flags, | 101 | Inst.BuiltinCall.Flags, |
| 102 | Inst.SwitchBlock.Bits, | 102 | Inst.SwitchBlock.Bits, |
| 103 | Inst.SwitchBlockErrUnion.Bits, | ||
| 103 | Inst.FuncFancy.Bits, | 104 | Inst.FuncFancy.Bits, |
| 104 | => @bitCast(code.extra[i]), | 105 | => @bitCast(code.extra[i]), |
| 105 | 106 | ||
| ... | @@ -277,9 +278,6 @@ pub const Inst = struct { | ... | @@ -277,9 +278,6 @@ pub const Inst = struct { |
| 277 | /// Create a `anyframe->T` type. | 278 | /// Create a `anyframe->T` type. |
| 278 | /// Uses the `un_node` field. | 279 | /// Uses the `un_node` field. |
| 279 | anyframe_type, | 280 | anyframe_type, |
| 280 | /// Type coercion. No source location attached. | ||
| 281 | /// Uses the `bin` field. | ||
| 282 | as, | ||
| 283 | /// Type coercion to the function's return type. | 281 | /// Type coercion to the function's return type. |
| 284 | /// Uses the `pl_node` field. Payload is `As`. AST node could be many things. | 282 | /// Uses the `pl_node` field. Payload is `As`. AST node could be many things. |
| 285 | as_node, | 283 | as_node, |
| ... | @@ -688,6 +686,9 @@ pub const Inst = struct { | ... | @@ -688,6 +686,9 @@ pub const Inst = struct { |
| 688 | /// A switch expression. Uses the `pl_node` union field. | 686 | /// A switch expression. Uses the `pl_node` union field. |
| 689 | /// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer. | 687 | /// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer. |
| 690 | switch_block_ref, | 688 | switch_block_ref, |
| 689 | /// A switch on an error union `a catch |err| switch (err) {...}`. | ||
| 690 | /// Uses the `pl_node` union field. AST node is the `catch`, payload is `SwitchBlockErrUnion`. | ||
| 691 | switch_block_err_union, | ||
| 691 | /// Check that operand type supports the dereference operand (.*). | 692 | /// Check that operand type supports the dereference operand (.*). |
| 692 | /// Uses the `un_node` field. | 693 | /// Uses the `un_node` field. |
| 693 | validate_deref, | 694 | validate_deref, |
| ... | @@ -1083,7 +1084,6 @@ pub const Inst = struct { | ... | @@ -1083,7 +1084,6 @@ pub const Inst = struct { |
| 1083 | .vector_elem_type, | 1084 | .vector_elem_type, |
| 1084 | .indexable_ptr_len, | 1085 | .indexable_ptr_len, |
| 1085 | .anyframe_type, | 1086 | .anyframe_type, |
| 1086 | .as, | ||
| 1087 | .as_node, | 1087 | .as_node, |
| 1088 | .as_shift_operand, | 1088 | .as_shift_operand, |
| 1089 | .bit_and, | 1089 | .bit_and, |
| ... | @@ -1190,6 +1190,7 @@ pub const Inst = struct { | ... | @@ -1190,6 +1190,7 @@ pub const Inst = struct { |
| 1190 | .set_eval_branch_quota, | 1190 | .set_eval_branch_quota, |
| 1191 | .switch_block, | 1191 | .switch_block, |
| 1192 | .switch_block_ref, | 1192 | .switch_block_ref, |
| 1193 | .switch_block_err_union, | ||
| 1193 | .validate_deref, | 1194 | .validate_deref, |
| 1194 | .validate_destructure, | 1195 | .validate_destructure, |
| 1195 | .union_init, | 1196 | .union_init, |
| ... | @@ -1396,7 +1397,6 @@ pub const Inst = struct { | ... | @@ -1396,7 +1397,6 @@ pub const Inst = struct { |
| 1396 | .vector_elem_type, | 1397 | .vector_elem_type, |
| 1397 | .indexable_ptr_len, | 1398 | .indexable_ptr_len, |
| 1398 | .anyframe_type, | 1399 | .anyframe_type, |
| 1399 | .as, | ||
| 1400 | .as_node, | 1400 | .as_node, |
| 1401 | .as_shift_operand, | 1401 | .as_shift_operand, |
| 1402 | .bit_and, | 1402 | .bit_and, |
| ... | @@ -1488,6 +1488,7 @@ pub const Inst = struct { | ... | @@ -1488,6 +1488,7 @@ pub const Inst = struct { |
| 1488 | .typeof_log2_int_type, | 1488 | .typeof_log2_int_type, |
| 1489 | .switch_block, | 1489 | .switch_block, |
| 1490 | .switch_block_ref, | 1490 | .switch_block_ref, |
| 1491 | .switch_block_err_union, | ||
| 1491 | .union_init, | 1492 | .union_init, |
| 1492 | .field_type_ref, | 1493 | .field_type_ref, |
| 1493 | .enum_from_int, | 1494 | .enum_from_int, |
| ... | @@ -1629,7 +1630,6 @@ pub const Inst = struct { | ... | @@ -1629,7 +1630,6 @@ pub const Inst = struct { |
| 1629 | .vector_elem_type = .un_node, | 1630 | .vector_elem_type = .un_node, |
| 1630 | .indexable_ptr_len = .un_node, | 1631 | .indexable_ptr_len = .un_node, |
| 1631 | .anyframe_type = .un_node, | 1632 | .anyframe_type = .un_node, |
| 1632 | .as = .bin, | ||
| 1633 | .as_node = .pl_node, | 1633 | .as_node = .pl_node, |
| 1634 | .as_shift_operand = .pl_node, | 1634 | .as_shift_operand = .pl_node, |
| 1635 | .bit_and = .pl_node, | 1635 | .bit_and = .pl_node, |
| ... | @@ -1741,6 +1741,7 @@ pub const Inst = struct { | ... | @@ -1741,6 +1741,7 @@ pub const Inst = struct { |
| 1741 | .enum_literal = .str_tok, | 1741 | .enum_literal = .str_tok, |
| 1742 | .switch_block = .pl_node, | 1742 | .switch_block = .pl_node, |
| 1743 | .switch_block_ref = .pl_node, | 1743 | .switch_block_ref = .pl_node, |
| 1744 | .switch_block_err_union = .pl_node, | ||
| 1744 | .validate_deref = .un_node, | 1745 | .validate_deref = .un_node, |
| 1745 | .validate_destructure = .pl_node, | 1746 | .validate_destructure = .pl_node, |
| 1746 | .field_type_ref = .pl_node, | 1747 | .field_type_ref = .pl_node, |
| ... | @@ -2782,6 +2783,29 @@ pub const Inst = struct { | ... | @@ -2782,6 +2783,29 @@ pub const Inst = struct { |
| 2782 | index: u32, | 2783 | index: u32, |
| 2783 | }; | 2784 | }; |
| 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 | /// 0. multi_cases_len: u32 // If has_multi_cases is set. | 2809 | /// 0. multi_cases_len: u32 // If has_multi_cases is set. |
| 2786 | /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture. | 2810 | /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture. |
| 2787 | /// 2. else_body { // If has_else or has_under is set. | 2811 | /// 2. else_body { // If has_else or has_under is set. |
| ... | @@ -2830,7 +2854,7 @@ pub const Inst = struct { | ... | @@ -2830,7 +2854,7 @@ pub const Inst = struct { |
| 2830 | }; | 2854 | }; |
| 2831 | }; | 2855 | }; |
| 2832 | 2856 | ||
| 2833 | pub const Bits = packed struct { | 2857 | pub const Bits = packed struct(u32) { |
| 2834 | /// If true, one or more prongs have multiple items. | 2858 | /// If true, one or more prongs have multiple items. |
| 2835 | has_multi_cases: bool, | 2859 | has_multi_cases: bool, |
| 2836 | /// If true, there is an else prong. This is mutually exclusive with `has_under`. | 2860 | /// If true, there is an else prong. This is mutually exclusive with `has_under`. |
src/print_zir.zig+139-1| ... | @@ -199,7 +199,6 @@ const Writer = struct { | ... | @@ -199,7 +199,6 @@ const Writer = struct { |
| 199 | const tag = tags[@intFromEnum(inst)]; | 199 | const tag = tags[@intFromEnum(inst)]; |
| 200 | try stream.print("= {s}(", .{@tagName(tags[@intFromEnum(inst)])}); | 200 | try stream.print("= {s}(", .{@tagName(tags[@intFromEnum(inst)])}); |
| 201 | switch (tag) { | 201 | switch (tag) { |
| 202 | .as, | ||
| 203 | .store, | 202 | .store, |
| 204 | .store_to_inferred_ptr, | 203 | .store_to_inferred_ptr, |
| 205 | => try self.writeBin(stream, inst), | 204 | => try self.writeBin(stream, inst), |
| ... | @@ -465,6 +464,8 @@ const Writer = struct { | ... | @@ -465,6 +464,8 @@ const Writer = struct { |
| 465 | .switch_block_ref, | 464 | .switch_block_ref, |
| 466 | => try self.writeSwitchBlock(stream, inst), | 465 | => try self.writeSwitchBlock(stream, inst), |
| 467 | 466 | ||
| 467 | .switch_block_err_union => try self.writeSwitchBlockErrUnion(stream, inst), | ||
| 468 | |||
| 468 | .field_val, | 469 | .field_val, |
| 469 | .field_ptr, | 470 | .field_ptr, |
| 470 | => try self.writePlNodeField(stream, inst), | 471 | => try self.writePlNodeField(stream, inst), |
| ... | @@ -2027,6 +2028,143 @@ const Writer = struct { | ... | @@ -2027,6 +2028,143 @@ const Writer = struct { |
| 2027 | try self.writeSrc(stream, inst_data.src()); | 2028 | try self.writeSrc(stream, inst_data.src()); |
| 2028 | } | 2029 | } |
| 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 | fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | 2168 | fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| 2031 | const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; | 2169 | const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; |
| 2032 | const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); | 2170 | const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); |
test/behavior/switch_on_captured_error.zig created+750| ... | @@ -0,0 +1,750 @@ | ||
| 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,8 +5,24 @@ fn f(x: u32) void { |
| 5 | else => true, | 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 | export fn entry() void { | 22 | export fn entry() void { |
| 9 | f(1234); | 23 | f(1234); |
| 24 | g(1234); | ||
| 25 | h(1234); | ||
| 10 | } | 26 | } |
| 11 | 27 | ||
| 12 | // error | 28 | // error |
| ... | @@ -15,3 +31,7 @@ export fn entry() void { | ... | @@ -15,3 +31,7 @@ export fn entry() void { |
| 15 | // | 31 | // |
| 16 | // :5:9: error: multiple else prongs in switch expression | 32 | // :5:9: error: multiple else prongs in switch expression |
| 17 | // :4:9: note: previous else prong here | 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,6 +23,13 @@ pub export fn entry() usize { |
| 23 | u += 1; | 23 | u += 1; |
| 24 | }, | 24 | }, |
| 25 | } | 25 | } |
| 26 | if (@as(error{}!usize, u)) |_| { | ||
| 27 | u += 1; | ||
| 28 | } else |e| switch (e) { | ||
| 29 | else => { | ||
| 30 | u += 1; | ||
| 31 | } | ||
| 32 | } | ||
| 26 | return u; | 33 | return u; |
| 27 | } | 34 | } |
| 28 | 35 |