authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-07-27 18:04:08+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-08-17 20:47:31+03:00
log1e835e0fccfd82deb31922ffdee821b0b418709f
tree87e520b7517faba7ce3222c03dbf5496105f1304
parent65185016f15ca69363113f85537542b0bdebe33f
signature Commit is signed but in an unrecognized format.

disallow '_' prong when switching on non-exhaustive tagged union

A tagged union cannot legally be initiated to an invalid enumeration

3 files changed, 64 insertions(+), 7 deletions(-)

src/ir.cpp+11-5
...@@ -14138,7 +14138,7 @@ static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr,...@@ -14138,7 +14138,7 @@ static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr,
14138 // If there is only 1 possible tag, then we know at comptime what it is.14138 // If there is only 1 possible tag, then we know at comptime what it is.
14139 if (wanted_type->data.enumeration.layout == ContainerLayoutAuto &&14139 if (wanted_type->data.enumeration.layout == ContainerLayoutAuto &&
14140 wanted_type->data.enumeration.src_field_count == 1 &&14140 wanted_type->data.enumeration.src_field_count == 1 &&
14141 !wanted_type->data.enumeration.non_exhaustive) // TODO are non-exhaustive union tag types supposed to be allowed?14141 !wanted_type->data.enumeration.non_exhaustive)
14142 {14142 {
14143 IrInstGen *result = ir_const(ira, source_instr, wanted_type);14143 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
14144 result->value->special = ConstValSpecialStatic;14144 result->value->special = ConstValSpecialStatic;
...@@ -23816,7 +23816,6 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -23816,7 +23816,6 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,
23816 bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag);23816 bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag);
23817 return result;23817 return result;
23818 }23818 }
23819 // TODO are non-exhaustive union tag types supposed to be allowed?
23820 if (tag_type->data.enumeration.src_field_count == 1 && !tag_type->data.enumeration.non_exhaustive) {23819 if (tag_type->data.enumeration.src_field_count == 1 && !tag_type->data.enumeration.non_exhaustive) {
23821 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type);23820 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type);
23822 TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];23821 TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];
...@@ -28839,6 +28838,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28839,6 +28838,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28839 if (type_is_invalid(switch_type))28838 if (type_is_invalid(switch_type))
28840 return ira->codegen->invalid_inst_gen;28839 return ira->codegen->invalid_inst_gen;
2884128840
28841 ZigValue *original_value = ((IrInstSrcSwitchTarget *)(instruction->target_value))->target_value_ptr->child->value;
28842 bool target_is_originally_union = original_value->type->id == ZigTypeIdPointer &&
28843 original_value->type->data.pointer.child_type->id == ZigTypeIdUnion;
28844
28842 if (switch_type->id == ZigTypeIdEnum) {28845 if (switch_type->id == ZigTypeIdEnum) {
28843 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> field_prev_uses = {};28846 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> field_prev_uses = {};
28844 field_prev_uses.init(switch_type->data.enumeration.src_field_count);28847 field_prev_uses.init(switch_type->data.enumeration.src_field_count);
...@@ -28894,9 +28897,12 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28894,9 +28897,12 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28894 }28897 }
28895 }28898 }
28896 if (instruction->have_underscore_prong) {28899 if (instruction->have_underscore_prong) {
28897 if (!switch_type->data.enumeration.non_exhaustive){28900 if (!switch_type->data.enumeration.non_exhaustive) {
28901 ir_add_error(ira, &instruction->base.base,
28902 buf_sprintf("switch on exhaustive enum has `_` prong"));
28903 } else if (target_is_originally_union) {
28898 ir_add_error(ira, &instruction->base.base,28904 ir_add_error(ira, &instruction->base.base,
28899 buf_sprintf("switch on non-exhaustive enum has `_` prong"));28905 buf_sprintf("`_` prong not allowed when switching on tagged union"));
28900 }28906 }
28901 for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {28907 for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
28902 TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];28908 TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];
...@@ -28911,7 +28917,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28911,7 +28917,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28911 }28917 }
28912 }28918 }
28913 } else if (instruction->else_prong == nullptr) {28919 } else if (instruction->else_prong == nullptr) {
28914 if (switch_type->data.enumeration.non_exhaustive) {28920 if (switch_type->data.enumeration.non_exhaustive && !target_is_originally_union) {
28915 ir_add_error(ira, &instruction->base.base,28921 ir_add_error(ira, &instruction->base.base,
28916 buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));28922 buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));
28917 }28923 }
test/compile_errors.zig+30-2
...@@ -51,6 +51,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -51,6 +51,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
51 "tmp.zig:17:23: error: cannot adjust alignment of zero sized type 'fn(u32) anytype'",51 "tmp.zig:17:23: error: cannot adjust alignment of zero sized type 'fn(u32) anytype'",
52 });52 });
5353
54 cases.addTest("switching with exhaustive enum has '_' prong ",
55 \\const E = enum{
56 \\ a,
57 \\ b,
58 \\};
59 \\pub export fn entry() void {
60 \\ var e: E = .b;
61 \\ switch (e) {
62 \\ .a => {},
63 \\ .b => {},
64 \\ _ => {},
65 \\ }
66 \\}
67 , &[_][]const u8{
68 "tmp.zig:7:5: error: switch on exhaustive enum has `_` prong",
69 });
70
54 cases.addTest("invalid pointer with @Type",71 cases.addTest("invalid pointer with @Type",
55 \\export fn entry() void {72 \\export fn entry() void {
56 \\ _ = @Type(.{ .Pointer = .{73 \\ _ = @Type(.{ .Pointer = .{
...@@ -564,6 +581,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -564,6 +581,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
564 \\ b,581 \\ b,
565 \\ _,582 \\ _,
566 \\};583 \\};
584 \\const U = union(E) {
585 \\ a: i32,
586 \\ b: u32,
587 \\};
567 \\pub export fn entry() void {588 \\pub export fn entry() void {
568 \\ var e: E = .b;589 \\ var e: E = .b;
569 \\ switch (e) { // error: switch not handling the tag `b`590 \\ switch (e) { // error: switch not handling the tag `b`
...@@ -574,10 +595,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -574,10 +595,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
574 \\ .a => {},595 \\ .a => {},
575 \\ .b => {},596 \\ .b => {},
576 \\ }597 \\ }
598 \\ var u = U{.a = 2};
599 \\ switch (u) { // error: `_` prong not allowed when switching on tagged union
600 \\ .a => {},
601 \\ .b => {},
602 \\ _ => {},
603 \\ }
577 \\}604 \\}
578 , &[_][]const u8{605 , &[_][]const u8{
579 "tmp.zig:8:5: error: enumeration value 'E.b' not handled in switch",606 "tmp.zig:12:5: error: enumeration value 'E.b' not handled in switch",
580 "tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong",607 "tmp.zig:16:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
608 "tmp.zig:21:5: error: `_` prong not allowed when switching on tagged union",
581 });609 });
582610
583 cases.add("switch expression - unreachable else prong (bool)",611 cases.add("switch expression - unreachable else prong (bool)",
test/stage1/behavior/union.zig+23
...@@ -690,3 +690,26 @@ test "method call on an empty union" {...@@ -690,3 +690,26 @@ test "method call on an empty union" {
690 S.doTheTest();690 S.doTheTest();
691 comptime S.doTheTest();691 comptime S.doTheTest();
692}692}
693
694test "switching on non exhaustive union" {
695 const S = struct {
696 const E = enum(u8) {
697 a,
698 b,
699 _,
700 };
701 const U = union(E) {
702 a: i32,
703 b: u32,
704 };
705 fn doTheTest() void {
706 var a = U{ .a = 2 };
707 switch (a) {
708 .a => |val| expect(val == 2),
709 .b => unreachable,
710 }
711 }
712 };
713 S.doTheTest();
714 comptime S.doTheTest();
715}