authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-08-18 23:55:44+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-08-18 23:55:44+03:00
logd139e44cfae68ace32458ff4d804dc6331b1ec8e
treed18d1f3ece51b6408172157d5f023e7a6c076afc
parente2c741f1e7dbddabdbfc18a2520f5efa376899bc
parent2948f2d262926725b4b8cb5beeb4fdba00b48336
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5495 from xackus/fix_5314

stage1: fix non-exhaustive enums with one field

5 files changed, 152 insertions(+), 11 deletions(-)

src/analyze.cpp+8-1
...@@ -7303,7 +7303,14 @@ void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val) {...@@ -7303,7 +7303,14 @@ void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val) {
7303 case ZigTypeIdEnum:7303 case ZigTypeIdEnum:
7304 {7304 {
7305 TypeEnumField *field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum_tag);7305 TypeEnumField *field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum_tag);
7306 buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name));7306 if(field != nullptr){
7307 buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name));
7308 } else {
7309 // untagged value in a non-exhaustive enum
7310 buf_appendf(buf, "%s.(", buf_ptr(&type_entry->name));
7311 bigint_append_buf(buf, &const_val->data.x_enum_tag, 10);
7312 buf_appendf(buf, ")");
7313 }
7307 return;7314 return;
7308 }7315 }
7309 case ZigTypeIdErrorUnion:7316 case ZigTypeIdErrorUnion:
src/ir.cpp+31-8
...@@ -14096,7 +14096,8 @@ static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, I...@@ -14096,7 +14096,8 @@ static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, I
1409614096
14097 // If there is only one possible tag, then we know at comptime what it is.14097 // If there is only one possible tag, then we know at comptime what it is.
14098 if (enum_type->data.enumeration.layout == ContainerLayoutAuto &&14098 if (enum_type->data.enumeration.layout == ContainerLayoutAuto &&
14099 enum_type->data.enumeration.src_field_count == 1)14099 enum_type->data.enumeration.src_field_count == 1 &&
14100 !enum_type->data.enumeration.non_exhaustive)
14100 {14101 {
14101 IrInstGen *result = ir_const(ira, source_instr, tag_type);14102 IrInstGen *result = ir_const(ira, source_instr, tag_type);
14102 init_const_bigint(result->value, tag_type,14103 init_const_bigint(result->value, tag_type,
...@@ -14136,7 +14137,8 @@ static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr,...@@ -14136,7 +14137,8 @@ static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr,
1413614137
14137 // 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.
14138 if (wanted_type->data.enumeration.layout == ContainerLayoutAuto &&14139 if (wanted_type->data.enumeration.layout == ContainerLayoutAuto &&
14139 wanted_type->data.enumeration.src_field_count == 1)14140 wanted_type->data.enumeration.src_field_count == 1 &&
14141 !wanted_type->data.enumeration.non_exhaustive)
14140 {14142 {
14141 IrInstGen *result = ir_const(ira, source_instr, wanted_type);14143 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
14142 result->value->special = ConstValSpecialStatic;14144 result->value->special = ConstValSpecialStatic;
...@@ -14175,7 +14177,14 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,...@@ -14175,7 +14177,14 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,
14175 if (!val)14177 if (!val)
14176 return ira->codegen->invalid_inst_gen;14178 return ira->codegen->invalid_inst_gen;
14177 TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);14179 TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);
14178 assert(union_field != nullptr);14180 if (union_field == nullptr) {
14181 Buf *int_buf = buf_alloc();
14182 bigint_append_buf(int_buf, &target->value->data.x_enum_tag, 10);
14183
14184 ir_add_error(ira, &target->base,
14185 buf_sprintf("no tag by value %s", buf_ptr(int_buf)));
14186 return ira->codegen->invalid_inst_gen;
14187 }
14179 ZigType *field_type = resolve_union_field_type(ira->codegen, union_field);14188 ZigType *field_type = resolve_union_field_type(ira->codegen, union_field);
14180 if (field_type == nullptr)14189 if (field_type == nullptr)
14181 return ira->codegen->invalid_inst_gen;14190 return ira->codegen->invalid_inst_gen;
...@@ -14211,6 +14220,13 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,...@@ -14211,6 +14220,13 @@ static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr,
14211 return result;14220 return result;
14212 }14221 }
1421314222
14223 if (target->value->type->data.enumeration.non_exhaustive) {
14224 ir_add_error(ira, source_instr,
14225 buf_sprintf("runtime cast to union '%s' from non-exhustive enum",
14226 buf_ptr(&wanted_type->name)));
14227 return ira->codegen->invalid_inst_gen;
14228 }
14229
14214 // if the union has all fields 0 bits, we can do it14230 // if the union has all fields 0 bits, we can do it
14215 // and in fact it's a noop cast because the union value is just the enum value14231 // and in fact it's a noop cast because the union value is just the enum value
14216 if (wanted_type->data.unionation.gen_field_count == 0) {14232 if (wanted_type->data.unionation.gen_field_count == 0) {
...@@ -23816,7 +23832,7 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -23816,7 +23832,7 @@ 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);23832 bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag);
23817 return result;23833 return result;
23818 }23834 }
23819 if (tag_type->data.enumeration.src_field_count == 1) {23835 if (tag_type->data.enumeration.src_field_count == 1 && !tag_type->data.enumeration.non_exhaustive) {
23820 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type);23836 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type);
23821 TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];23837 TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];
23822 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);23838 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);
...@@ -23831,7 +23847,7 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -23831,7 +23847,7 @@ static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira,
23831 case ZigTypeIdEnum: {23847 case ZigTypeIdEnum: {
23832 if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))23848 if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))
23833 return ira->codegen->invalid_inst_gen;23849 return ira->codegen->invalid_inst_gen;
23834 if (target_type->data.enumeration.src_field_count == 1) {23850 if (target_type->data.enumeration.src_field_count == 1 && !target_type->data.enumeration.non_exhaustive) {
23835 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];23851 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
23836 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type);23852 IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type);
23837 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);23853 bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value);
...@@ -28838,6 +28854,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28838,6 +28854,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28838 if (type_is_invalid(switch_type))28854 if (type_is_invalid(switch_type))
28839 return ira->codegen->invalid_inst_gen;28855 return ira->codegen->invalid_inst_gen;
2884028856
28857 ZigValue *original_value = ((IrInstSrcSwitchTarget *)(instruction->target_value))->target_value_ptr->child->value;
28858 bool target_is_originally_union = original_value->type->id == ZigTypeIdPointer &&
28859 original_value->type->data.pointer.child_type->id == ZigTypeIdUnion;
28860
28841 if (switch_type->id == ZigTypeIdEnum) {28861 if (switch_type->id == ZigTypeIdEnum) {
28842 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> field_prev_uses = {};28862 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> field_prev_uses = {};
28843 field_prev_uses.init(switch_type->data.enumeration.src_field_count);28863 field_prev_uses.init(switch_type->data.enumeration.src_field_count);
...@@ -28893,9 +28913,12 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28893,9 +28913,12 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28893 }28913 }
28894 }28914 }
28895 if (instruction->have_underscore_prong) {28915 if (instruction->have_underscore_prong) {
28896 if (!switch_type->data.enumeration.non_exhaustive){28916 if (!switch_type->data.enumeration.non_exhaustive) {
28917 ir_add_error(ira, &instruction->base.base,
28918 buf_sprintf("switch on exhaustive enum has `_` prong"));
28919 } else if (target_is_originally_union) {
28897 ir_add_error(ira, &instruction->base.base,28920 ir_add_error(ira, &instruction->base.base,
28898 buf_sprintf("switch on non-exhaustive enum has `_` prong"));28921 buf_sprintf("`_` prong not allowed when switching on tagged union"));
28899 }28922 }
28900 for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {28923 for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
28901 TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];28924 TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];
...@@ -28910,7 +28933,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28910,7 +28933,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28910 }28933 }
28911 }28934 }
28912 } else if (instruction->else_prong == nullptr) {28935 } else if (instruction->else_prong == nullptr) {
28913 if (switch_type->data.enumeration.non_exhaustive) {28936 if (switch_type->data.enumeration.non_exhaustive && !target_is_originally_union) {
28914 ir_add_error(ira, &instruction->base.base,28937 ir_add_error(ira, &instruction->base.base,
28915 buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));28938 buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));
28916 }28939 }
test/compile_errors.zig+53-2
...@@ -51,6 +51,46 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -51,6 +51,46 @@ 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("invalid non-exhaustive enum to union",
55 \\const E = enum(u8) {
56 \\ a,
57 \\ b,
58 \\ _,
59 \\};
60 \\const U = union(E) {
61 \\ a,
62 \\ b,
63 \\};
64 \\export fn foo() void {
65 \\ var e = @intToEnum(E, 15);
66 \\ var u: U = e;
67 \\}
68 \\export fn bar() void {
69 \\ const e = @intToEnum(E, 15);
70 \\ var u: U = e;
71 \\}
72 , &[_][]const u8{
73 "tmp.zig:12:16: error: runtime cast to union 'U' from non-exhustive enum",
74 "tmp.zig:16:16: error: no tag by value 15",
75 });
76
77 cases.addTest("switching with exhaustive enum has '_' prong ",
78 \\const E = enum{
79 \\ a,
80 \\ b,
81 \\};
82 \\pub export fn entry() void {
83 \\ var e: E = .b;
84 \\ switch (e) {
85 \\ .a => {},
86 \\ .b => {},
87 \\ _ => {},
88 \\ }
89 \\}
90 , &[_][]const u8{
91 "tmp.zig:7:5: error: switch on exhaustive enum has `_` prong",
92 });
93
54 cases.addTest("invalid pointer with @Type",94 cases.addTest("invalid pointer with @Type",
55 \\export fn entry() void {95 \\export fn entry() void {
56 \\ _ = @Type(.{ .Pointer = .{96 \\ _ = @Type(.{ .Pointer = .{
...@@ -564,6 +604,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -564,6 +604,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
564 \\ b,604 \\ b,
565 \\ _,605 \\ _,
566 \\};606 \\};
607 \\const U = union(E) {
608 \\ a: i32,
609 \\ b: u32,
610 \\};
567 \\pub export fn entry() void {611 \\pub export fn entry() void {
568 \\ var e: E = .b;612 \\ var e: E = .b;
569 \\ switch (e) { // error: switch not handling the tag `b`613 \\ switch (e) { // error: switch not handling the tag `b`
...@@ -574,10 +618,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -574,10 +618,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
574 \\ .a => {},618 \\ .a => {},
575 \\ .b => {},619 \\ .b => {},
576 \\ }620 \\ }
621 \\ var u = U{.a = 2};
622 \\ switch (u) { // error: `_` prong not allowed when switching on tagged union
623 \\ .a => {},
624 \\ .b => {},
625 \\ _ => {},
626 \\ }
577 \\}627 \\}
578 , &[_][]const u8{628 , &[_][]const u8{
579 "tmp.zig:8:5: error: enumeration value 'E.b' not handled in switch",629 "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",630 "tmp.zig:16:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
631 "tmp.zig:21:5: error: `_` prong not allowed when switching on tagged union",
581 });632 });
582633
583 cases.add("switch expression - unreachable else prong (bool)",634 cases.add("switch expression - unreachable else prong (bool)",
test/stage1/behavior/enum.zig+37
...@@ -85,6 +85,43 @@ test "empty non-exhaustive enum" {...@@ -85,6 +85,43 @@ test "empty non-exhaustive enum" {
85 comptime S.doTheTest(42);85 comptime S.doTheTest(42);
86}86}
8787
88test "single field non-exhaustive enum" {
89 const S = struct {
90 const E = enum(u8) {
91 a,
92 _,
93 };
94 fn doTheTest(y: u8) void {
95 var e: E = .a;
96 expect(switch (e) {
97 .a => true,
98 _ => false,
99 });
100 e = @intToEnum(E, 12);
101 expect(switch (e) {
102 .a => false,
103 _ => true,
104 });
105
106 expect(switch (e) {
107 .a => false,
108 else => true,
109 });
110 e = .a;
111 expect(switch (e) {
112 .a => true,
113 else => false,
114 });
115
116 expect(@enumToInt(@intToEnum(E, y)) == y);
117 expect(@typeInfo(E).Enum.fields.len == 1);
118 expect(@typeInfo(E).Enum.is_exhaustive == false);
119 }
120 };
121 S.doTheTest(23);
122 comptime S.doTheTest(23);
123}
124
88test "enum type" {125test "enum type" {
89 const foo1 = Foo{ .One = 13 };126 const foo1 = Foo{ .One = 13 };
90 const foo2 = Foo{127 const foo2 = Foo{
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}