| author | |
| committer | |
| log | bfe0bf695b8471a5553bf5cdf5fc527c42eda1e8 |
| tree | 028b4b233b826f31a6ffc95c8d58a7314529c904 |
| parent | c2cf04086a74a3c0ac31dbc1b8d01de76988c7ae |
| parent | a1b952f4b03b7becad85ad47f96a75c0be620cf8 |
| signature |
4 files changed, 73 insertions(+), 6 deletions(-)
doc/langref.html.in+4-2| ... | ... | @@ -3016,6 +3016,7 @@ test "switch on tagged union" { |
| 3016 | 3016 | A: u32, |
| 3017 | 3017 | C: Point, |
| 3018 | 3018 | D, |
| 3019 | E: u32, | |
| 3019 | 3020 | }; |
| 3020 | 3021 | |
| 3021 | 3022 | var a = Item{ .C = Point{ .x = 1, .y = 2 } }; |
| ... | ... | @@ -3023,8 +3024,9 @@ test "switch on tagged union" { |
| 3023 | 3024 | // Switching on more complex enums is allowed. |
| 3024 | 3025 | const b = switch (a) { |
| 3025 | 3026 | // A capture group is allowed on a match, and will return the enum |
| 3026 | // value matched. | |
| 3027 | Item.A => |item| item, | |
| 3027 | // value matched. If the payloads of both cases are the same | |
| 3028 | // they can be put into the same switch prong. | |
| 3029 | Item.A, Item.E => |item| item, | |
| 3028 | 3030 | |
| 3029 | 3031 | // A reference to the matched value can be obtained using `*` syntax. |
| 3030 | 3032 | Item.C => |*item| blk: { |
src/ir.cpp+34-4| ... | ... | @@ -19230,10 +19230,6 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru |
| 19230 | 19230 | assert(enum_type != nullptr); |
| 19231 | 19231 | assert(enum_type->id == ZigTypeIdEnum); |
| 19232 | 19232 | |
| 19233 | if (instruction->prongs_len != 1) { | |
| 19234 | return target_value_ptr; | |
| 19235 | } | |
| 19236 | ||
| 19237 | 19233 | IrInstruction *prong_value = instruction->prongs_ptr[0]->child; |
| 19238 | 19234 | if (type_is_invalid(prong_value->value.type)) |
| 19239 | 19235 | return ira->codegen->invalid_instruction; |
| ... | ... | @@ -19248,6 +19244,40 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru |
| 19248 | 19244 | |
| 19249 | 19245 | TypeUnionField *field = find_union_field_by_tag(target_type, &prong_val->data.x_enum_tag); |
| 19250 | 19246 | |
| 19247 | if (instruction->prongs_len != 1) { | |
| 19248 | ErrorMsg *invalid_payload = nullptr; | |
| 19249 | Buf *invalid_payload_list = nullptr; | |
| 19250 | ||
| 19251 | for (size_t i = 1; i < instruction->prongs_len; i++) { | |
| 19252 | IrInstruction *casted_prong_value = ir_implicit_cast(ira, instruction->prongs_ptr[i]->child, enum_type); | |
| 19253 | if (type_is_invalid(casted_prong_value->value.type)) | |
| 19254 | return ira->codegen->invalid_instruction; | |
| 19255 | ||
| 19256 | ConstExprValue *next_prong = ir_resolve_const(ira, casted_prong_value, UndefBad); | |
| 19257 | if (!next_prong) | |
| 19258 | return ira->codegen->invalid_instruction; | |
| 19259 | ||
| 19260 | ZigType *payload = find_union_field_by_tag(target_type, &next_prong->data.x_enum_tag)->type_entry; | |
| 19261 | ||
| 19262 | if (field->type_entry != payload) { | |
| 19263 | if (!invalid_payload) { | |
| 19264 | invalid_payload = ir_add_error(ira, &instruction->base, | |
| 19265 | buf_sprintf("switch prong contains cases with different payloads")); | |
| 19266 | invalid_payload_list = buf_sprintf("payload types are %s", buf_ptr(&field->type_entry->name)); | |
| 19267 | } | |
| 19268 | ||
| 19269 | if (i == instruction->prongs_len - 1) | |
| 19270 | buf_append_buf(invalid_payload_list, buf_sprintf(" and %s", buf_ptr(&payload->name))); | |
| 19271 | else | |
| 19272 | buf_append_buf(invalid_payload_list, buf_sprintf(", %s", buf_ptr(&payload->name))); | |
| 19273 | } | |
| 19274 | } | |
| 19275 | ||
| 19276 | if (invalid_payload) | |
| 19277 | add_error_note(ira->codegen, invalid_payload, | |
| 19278 | ((IrInstruction*)instruction)->source_node, invalid_payload_list); | |
| 19279 | } | |
| 19280 | ||
| 19251 | 19281 | if (instr_is_comptime(target_value_ptr)) { |
| 19252 | 19282 | ConstExprValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad); |
| 19253 | 19283 | if (!target_value_ptr) |
test/compile_errors.zig+17| ... | ... | @@ -6073,4 +6073,21 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 6073 | 6073 | "tmp.zig:5:30: error: expression value is ignored", |
| 6074 | 6074 | "tmp.zig:9:30: error: expression value is ignored", |
| 6075 | 6075 | ); |
| 6076 | ||
| 6077 | cases.add( | |
| 6078 | "capture group on switch prong with different payloads", | |
| 6079 | \\const Union = union(enum) { | |
| 6080 | \\ A: usize, | |
| 6081 | \\ B: isize, | |
| 6082 | \\}; | |
| 6083 | \\comptime { | |
| 6084 | \\ var u = Union{ .A = 8 }; | |
| 6085 | \\ switch (u) { | |
| 6086 | \\ .A, .B => |e| unreachable, | |
| 6087 | \\ } | |
| 6088 | \\} | |
| 6089 | , | |
| 6090 | "tmp.zig:8:20: error: switch prong contains cases with different payloads", | |
| 6091 | "tmp.zig:8:20: note: payload types are usize and isize", | |
| 6092 | ); | |
| 6076 | 6093 | } |
test/stage1/behavior/switch.zig+18| ... | ... | @@ -391,3 +391,21 @@ test "switch with null and T peer types and inferred result location type" { |
| 391 | 391 | S.doTheTest(1); |
| 392 | 392 | comptime S.doTheTest(1); |
| 393 | 393 | } |
| 394 | ||
| 395 | test "switch prongs with cases with identical payloads" { | |
| 396 | const Union = union(enum) { | |
| 397 | A: usize, | |
| 398 | B: isize, | |
| 399 | C: usize, | |
| 400 | }; | |
| 401 | const S = struct { | |
| 402 | fn doTheTest(u: Union) void { | |
| 403 | switch (u) { | |
| 404 | .A, .C => |e| expect(@typeOf(e) == usize), | |
| 405 | .B => |e| expect(@typeOf(e) == isize), | |
| 406 | } | |
| 407 | } | |
| 408 | }; | |
| 409 | S.doTheTest(Union{ .A = 8 }); | |
| 410 | comptime S.doTheTest(Union{ .B = -8 }); | |
| 411 | } |