authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2019-05-09 18:40:00+02:00
committergravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2019-05-11 21:28:58+02:00
logc766f3f9ca84b81a9f5669dd5132e46355ef284b
treef039bb98ae920efb0c9fecf7ccde6355567c01b9
parent917bd4192d4586fb2e8f3855d9e2e2eb0bf45921

Support signed types as enum tags


3 files changed, 45 insertions(+), 42 deletions(-)

src/analyze.cpp+32-25
...@@ -2005,15 +2005,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2005,15 +2005,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2005 buf_ptr(&wanted_tag_int_type->name)));2005 buf_ptr(&wanted_tag_int_type->name)));
2006 add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr,2006 add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr,
2007 buf_sprintf("valid types are 'i8', 'c_int' and 'c_uint' or compatible types"));2007 buf_sprintf("valid types are 'i8', 'c_int' and 'c_uint' or compatible types"));
2008 } else if (wanted_tag_int_type->data.integral.is_signed) {
2009 enum_type->data.enumeration.is_invalid = true;
2010 add_node_error(g, decl_node->data.container_decl.init_arg_expr,
2011 buf_sprintf("expected unsigned integer, found '%s'", buf_ptr(&wanted_tag_int_type->name)));
2012 } else if (wanted_tag_int_type->data.integral.bit_count < tag_int_type->data.integral.bit_count) {
2013 enum_type->data.enumeration.is_invalid = true;
2014 add_node_error(g, decl_node->data.container_decl.init_arg_expr,
2015 buf_sprintf("'%s' too small to hold all bits; must be at least '%s'",
2016 buf_ptr(&wanted_tag_int_type->name), buf_ptr(&tag_int_type->name)));
2017 } else {2008 } else {
2018 tag_int_type = wanted_tag_int_type;2009 tag_int_type = wanted_tag_int_type;
2019 }2010 }
...@@ -2078,30 +2069,46 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2078,30 +2069,46 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2078 }2069 }
20792070
2080 // Now iterate again and populate the unspecified tag values2071 // Now iterate again and populate the unspecified tag values
2081 uint32_t next_maybe_unoccupied_index = 0;2072 BigInt next_maybe_unoccupied_index;
2073 bigint_init_unsigned(&next_maybe_unoccupied_index, 0);
2074
2075 // Since we're allocating positive values only we have one less bit
2076 // available if the tag type is signed (eg. for a i8 we can only use (0,127))
2077 unsigned tag_bit_width = tag_int_type->size_in_bits;
2078 if (tag_int_type->data.integral.is_signed)
2079 tag_bit_width--;
20822080
2083 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {2081 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
2084 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);2082 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
2085 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];2083 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
2086 AstNode *tag_value = field_node->data.struct_field.value;2084 AstNode *tag_value = field_node->data.struct_field.value;
20872085
2088 if (tag_value == nullptr) {2086 // Already handled in the loop above
2089 if (occupied_tag_values.size() == 0) {2087 if (tag_value != nullptr)
2090 bigint_init_unsigned(&type_enum_field->value, next_maybe_unoccupied_index);2088 continue;
2091 next_maybe_unoccupied_index += 1;2089
2092 } else {2090 // Make sure we can represent this number with tag_int_type
2093 BigInt proposed_value;2091 const unsigned repr_bits = bigint_bits_needed(&next_maybe_unoccupied_index);
2094 for (;;) {2092 if (repr_bits > tag_bit_width) {
2095 bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index);2093 enum_type->data.enumeration.is_invalid = true;
2096 next_maybe_unoccupied_index += 1;2094 add_node_error(g, field_node,
2097 auto entry = occupied_tag_values.put_unique(proposed_value, field_node);2095 buf_sprintf("enumeration value %" ZIG_PRI_u64 " too large for type '%s'",
2098 if (entry != nullptr) {2096 bigint_as_unsigned(&next_maybe_unoccupied_index),
2099 continue;2097 buf_ptr(&tag_int_type->name)));
2100 }2098 break;
2099 }
2100
2101 if (occupied_tag_values.size() == 0) {
2102 type_enum_field->value = next_maybe_unoccupied_index;
2103 bigint_incr(&next_maybe_unoccupied_index);
2104 } else {
2105 for (;;) {
2106 auto entry = occupied_tag_values.put_unique(next_maybe_unoccupied_index, field_node);
2107 if (entry == nullptr)
2101 break;2108 break;
2102 }2109 bigint_incr(&next_maybe_unoccupied_index);
2103 bigint_init_bigint(&type_enum_field->value, &proposed_value);
2104 }2110 }
2111 type_enum_field->value = next_maybe_unoccupied_index;
2105 }2112 }
2106 }2113 }
21072114
test/compile_errors.zig+1-17
...@@ -5397,7 +5397,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -5397,7 +5397,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
5397 \\ var x = Small.One;5397 \\ var x = Small.One;
5398 \\}5398 \\}
5399 ,5399 ,
5400 "tmp.zig:1:21: error: 'u2' too small to hold all bits; must be at least 'u3'",5400 "tmp.zig:6:5: error: enumeration value 4 too large for type 'u2'"
5401 );5401 );
54025402
5403 cases.add(5403 cases.add(
...@@ -5448,22 +5448,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -5448,22 +5448,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
5448 "tmp.zig:10:31: error: expected type 'u2', found 'u3'",5448 "tmp.zig:10:31: error: expected type 'u2', found 'u3'",
5449 );5449 );
54505450
5451 cases.add(
5452 "non unsigned integer enum tag type",
5453 \\const Small = enum(i2) {
5454 \\ One,
5455 \\ Two,
5456 \\ Three,
5457 \\ Four,
5458 \\};
5459 \\
5460 \\export fn entry() void {
5461 \\ var y = Small.Two;
5462 \\}
5463 ,
5464 "tmp.zig:1:20: error: expected unsigned integer, found 'i2'",
5465 );
5466
5467 cases.add(5451 cases.add(
5468 "struct fields with value assignments",5452 "struct fields with value assignments",
5469 \\const MultipleChoice = struct {5453 \\const MultipleChoice = struct {
test/stage1/behavior/enum.zig+12
...@@ -938,3 +938,15 @@ test "enum literal in array literal" {...@@ -938,3 +938,15 @@ test "enum literal in array literal" {
938 expect(array[0] == .one);938 expect(array[0] == .one);
939 expect(array[1] == .two);939 expect(array[1] == .two);
940}940}
941
942test "signed integer as enum tag" {
943 const SignedEnum = enum (i2) {
944 A0 = -1,
945 A1 = 0,
946 A2 = 1,
947 };
948
949 expect(@enumToInt(SignedEnum.A0) == -1);
950 expect(@enumToInt(SignedEnum.A1) == 0);
951 expect(@enumToInt(SignedEnum.A2) == 1);
952}