From 2d85ff94653457ea12cd9ab0984ab3b13c9b41e5 Mon Sep 17 00:00:00 2001 From: emekoi Date: Mon, 1 Jul 2019 00:27:55 -0500 Subject: [PATCH 1/2] added invalid switch prong error added error for switch prong with different payloads and support for capturing payload on switch prongs with payloads of the same type --- src/ir.cpp | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index abae52fcb..d4cb5f90d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19128,10 +19128,6 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru assert(enum_type != nullptr); assert(enum_type->id == ZigTypeIdEnum); - if (instruction->prongs_len != 1) { - return target_value_ptr; - } - IrInstruction *prong_value = instruction->prongs_ptr[0]->child; if (type_is_invalid(prong_value->value.type)) return ira->codegen->invalid_instruction; @@ -19146,6 +19142,40 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru TypeUnionField *field = find_union_field_by_tag(target_type, &prong_val->data.x_enum_tag); + if (instruction->prongs_len != 1) { + ErrorMsg *invalid_payload = nullptr; + Buf *invalid_payload_list = nullptr; + + for (size_t i = 1; i < instruction->prongs_len; i++) { + IrInstruction *casted_prong_value = ir_implicit_cast(ira, instruction->prongs_ptr[i]->child, enum_type); + if (type_is_invalid(casted_prong_value->value.type)) + return ira->codegen->invalid_instruction; + + ConstExprValue *next_prong = ir_resolve_const(ira, casted_prong_value, UndefBad); + if (!next_prong) + return ira->codegen->invalid_instruction; + + ZigType *payload = find_union_field_by_tag(target_type, &next_prong->data.x_enum_tag)->type_entry; + + if (field->type_entry != payload) { + if (!invalid_payload) { + invalid_payload = ir_add_error(ira, &instruction->base, + buf_sprintf("switch prong contains cases with differing payloads")); + invalid_payload_list = buf_sprintf("types %s", buf_ptr(&field->type_entry->name)); + } + + if (i == instruction->prongs_len - 1) + buf_append_buf(invalid_payload_list, buf_sprintf(" and %s", buf_ptr(&payload->name))); + else + buf_append_buf(invalid_payload_list, buf_sprintf(", %s", buf_ptr(&payload->name))); + } + } + + if (invalid_payload) + add_error_note(ira->codegen, invalid_payload, + ((IrInstruction*)instruction)->source_node, invalid_payload_list); + } + if (instr_is_comptime(target_value_ptr)) { ConstExprValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad); if (!target_value_ptr) From a1b952f4b03b7becad85ad47f96a75c0be620cf8 Mon Sep 17 00:00:00 2001 From: emekoi Date: Wed, 3 Jul 2019 13:12:14 -0500 Subject: [PATCH 2/2] added tests for #1107 and a note in the reference --- doc/langref.html.in | 6 ++++-- src/ir.cpp | 4 ++-- test/compile_errors.zig | 17 +++++++++++++++++ test/stage1/behavior/switch.zig | 18 ++++++++++++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 4bd594c11..3b8e77595 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2973,6 +2973,7 @@ test "switch on tagged union" { A: u32, C: Point, D, + E: u32, }; var a = Item{ .C = Point{ .x = 1, .y = 2 } }; @@ -2980,8 +2981,9 @@ test "switch on tagged union" { // Switching on more complex enums is allowed. const b = switch (a) { // A capture group is allowed on a match, and will return the enum - // value matched. - Item.A => |item| item, + // value matched. If the payloads of both cases are the same + // they can be put into the same switch prong. + Item.A, Item.E => |item| item, // A reference to the matched value can be obtained using `*` syntax. Item.C => |*item| blk: { diff --git a/src/ir.cpp b/src/ir.cpp index d4cb5f90d..83155da4c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19160,8 +19160,8 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru if (field->type_entry != payload) { if (!invalid_payload) { invalid_payload = ir_add_error(ira, &instruction->base, - buf_sprintf("switch prong contains cases with differing payloads")); - invalid_payload_list = buf_sprintf("types %s", buf_ptr(&field->type_entry->name)); + buf_sprintf("switch prong contains cases with different payloads")); + invalid_payload_list = buf_sprintf("payload types are %s", buf_ptr(&field->type_entry->name)); } if (i == instruction->prongs_len - 1) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 94cd152eb..592870c67 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -6048,4 +6048,21 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:5:30: error: expression value is ignored", "tmp.zig:9:30: error: expression value is ignored", ); + + cases.add( + "capture group on switch prong with different payloads", + \\const Union = union(enum) { + \\ A: usize, + \\ B: isize, + \\}; + \\comptime { + \\ var u = Union{ .A = 8 }; + \\ switch (u) { + \\ .A, .B => |e| unreachable, + \\ } + \\} + , + "tmp.zig:8:20: error: switch prong contains cases with different payloads", + "tmp.zig:8:20: note: payload types are usize and isize", + ); } diff --git a/test/stage1/behavior/switch.zig b/test/stage1/behavior/switch.zig index 12e026d0b..2b7422fa6 100644 --- a/test/stage1/behavior/switch.zig +++ b/test/stage1/behavior/switch.zig @@ -391,3 +391,21 @@ test "switch with null and T peer types and inferred result location type" { S.doTheTest(1); comptime S.doTheTest(1); } + +test "switch prongs with cases with identical payloads" { + const Union = union(enum) { + A: usize, + B: isize, + C: usize, + }; + const S = struct { + fn doTheTest(u: Union) void { + switch (u) { + .A, .C => |e| expect(@typeOf(e) == usize), + .B => |e| expect(@typeOf(e) == isize), + } + } + }; + S.doTheTest(Union{ .A = 8 }); + comptime S.doTheTest(Union{ .B = -8 }); +}