authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2019-05-10 19:28:13+02:00
committergravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2019-05-11 21:29:53+02:00
logb05e8d46ec0a1a26c533118d5a0bab2262a99a63
treeb63f9f410579d34859c08d6c0d209a92f11ed0f7
parent655794f44fc8563f9fa4d45c208e859382a6a599

Change the enum value allocation strategy


3 files changed, 74 insertions(+), 65 deletions(-)

src/analyze.cpp+40-53
...@@ -2003,6 +2003,11 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2003,6 +2003,11 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2003 enum_type->abi_size = tag_int_type->abi_size;2003 enum_type->abi_size = tag_int_type->abi_size;
2004 enum_type->abi_align = tag_int_type->abi_align;2004 enum_type->abi_align = tag_int_type->abi_align;
20052005
2006 BigInt bi_one;
2007 bigint_init_unsigned(&bi_one, 1);
2008
2009 TypeEnumField *last_enum_field = nullptr;
2010
2006 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {2011 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
2007 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);2012 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
2008 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];2013 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
...@@ -2028,76 +2033,58 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2028,76 +2033,58 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
20282033
2029 AstNode *tag_value = field_node->data.struct_field.value;2034 AstNode *tag_value = field_node->data.struct_field.value;
20302035
2031 // In this first pass we resolve explicit tag values.
2032 // In a second pass we will fill in the unspecified ones.
2033 if (tag_value != nullptr) {2036 if (tag_value != nullptr) {
2037 // A user-specified value is available
2034 ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr);2038 ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr);
2035 if (type_is_invalid(result->type)) {2039 if (type_is_invalid(result->type)) {
2036 enum_type->data.enumeration.is_invalid = true;2040 enum_type->data.enumeration.is_invalid = true;
2037 continue;2041 continue;
2038 }2042 }
2043
2039 assert(result->special != ConstValSpecialRuntime);2044 assert(result->special != ConstValSpecialRuntime);
2040 assert(result->type->id == ZigTypeIdInt ||2045 assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt);
2041 result->type->id == ZigTypeIdComptimeInt);2046
2042 auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value);2047 bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);
2043 if (entry == nullptr) {2048 } else {
2044 bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);2049 // No value was explicitly specified: allocate the last value + 1
2050 // or, if this is the first element, zero
2051 if (last_enum_field != nullptr) {
2052 bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one);
2045 } else {2053 } else {
2046 Buf *val_buf = buf_alloc();2054 bigint_init_unsigned(&type_enum_field->value, 0);
2047 bigint_append_buf(val_buf, &result->data.x_bigint, 10);2055 }
20482056
2049 ErrorMsg *msg = add_node_error(g, tag_value,2057 // Make sure we can represent this number with tag_int_type
2050 buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));2058 if (!bigint_fits_in_bits(&type_enum_field->value,
2051 add_error_note(g, msg, entry->value,2059 tag_int_type->size_in_bits,
2052 buf_sprintf("other occurrence here"));2060 tag_int_type->data.integral.is_signed)) {
2053 enum_type->data.enumeration.is_invalid = true;2061 enum_type->data.enumeration.is_invalid = true;
2054 continue;
2055 }
2056 }
2057 }
20582062
2059 // Now iterate again and populate the unspecified tag values2063 Buf *val_buf = buf_alloc();
2060 BigInt next_maybe_unoccupied_index;2064 bigint_append_buf(val_buf, &type_enum_field->value, 10);
2061 bigint_init_unsigned(&next_maybe_unoccupied_index, 0);2065 add_node_error(g, field_node,
2066 buf_sprintf("enumeration value %s too large for type '%s'",
2067 buf_ptr(val_buf), buf_ptr(&tag_int_type->name)));
20622068
2063 // Since we're allocating positive values only we have one less bit2069 break;
2064 // available if the tag type is signed (eg. for a i8 we can only use (0,127))2070 }
2065 unsigned tag_bit_width = tag_int_type->size_in_bits;2071 }
2066 if (tag_int_type->data.integral.is_signed)
2067 tag_bit_width--;
20682072
2069 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {2073 // Make sure the value is unique
2070 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);2074 auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node);
2071 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];2075 if (entry != nullptr) {
2072 AstNode *tag_value = field_node->data.struct_field.value;2076 enum_type->data.enumeration.is_invalid = true;
20732077
2074 // Already handled in the loop above2078 Buf *val_buf = buf_alloc();
2075 if (tag_value != nullptr)2079 bigint_append_buf(val_buf, &type_enum_field->value, 10);
2076 continue;
20772080
2078 // Make sure we can represent this number with tag_int_type2081 ErrorMsg *msg = add_node_error(g, field_node,
2079 const unsigned repr_bits = bigint_bits_needed(&next_maybe_unoccupied_index);2082 buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
2080 if (repr_bits > tag_bit_width) {2083 add_error_note(g, msg, entry->value,
2081 enum_type->data.enumeration.is_invalid = true;2084 buf_sprintf("other occurrence here"));
2082 add_node_error(g, field_node,
2083 buf_sprintf("enumeration value %" ZIG_PRI_u64 " too large for type '%s'",
2084 bigint_as_unsigned(&next_maybe_unoccupied_index),
2085 buf_ptr(&tag_int_type->name)));
2086 break;
2087 }2085 }
20882086
2089 if (occupied_tag_values.size() == 0) {2087 last_enum_field = type_enum_field;
2090 type_enum_field->value = next_maybe_unoccupied_index;
2091 bigint_incr(&next_maybe_unoccupied_index);
2092 } else {
2093 for (;;) {
2094 auto entry = occupied_tag_values.put_unique(next_maybe_unoccupied_index, field_node);
2095 if (entry == nullptr)
2096 break;
2097 bigint_incr(&next_maybe_unoccupied_index);
2098 }
2099 type_enum_field->value = next_maybe_unoccupied_index;
2100 }
2101 }2088 }
21022089
2103 enum_type->data.enumeration.zero_bits_loop_flag = false;2090 enum_type->data.enumeration.zero_bits_loop_flag = false;
test/compile_errors.zig+17-7
...@@ -12,6 +12,19 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -12,6 +12,19 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
12 "tmp.zig:3:32: error: cast discards const qualifier",12 "tmp.zig:3:32: error: cast discards const qualifier",
13 );13 );
1414
15 cases.add(
16 "overflow in enum value allocation",
17 \\const Moo = enum(u8) {
18 \\ Last = 255,
19 \\ Over,
20 \\};
21 \\pub fn main() void {
22 \\ var y = Moo.Last;
23 \\}
24 ,
25 "tmp.zig:3:5: error: enumeration value 256 too large for type 'u8'",
26 );
27
15 cases.add(28 cases.add(
16 "attempt to cast enum literal to error",29 "attempt to cast enum literal to error",
17 \\export fn entry() void {30 \\export fn entry() void {
...@@ -5383,8 +5396,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -5383,8 +5396,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
5383 "tmp.zig:12:20: note: referenced here",5396 "tmp.zig:12:20: note: referenced here",
5384 );5397 );
53855398
5386 cases.add(5399 cases.add("specify enum tag type that is too small",
5387 "specify enum tag type that is too small",
5388 \\const Small = enum (u2) {5400 \\const Small = enum (u2) {
5389 \\ One,5401 \\ One,
5390 \\ Two,5402 \\ Two,
...@@ -5396,9 +5408,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -5396,9 +5408,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
5396 \\export fn entry() void {5408 \\export fn entry() void {
5397 \\ var x = Small.One;5409 \\ var x = Small.One;
5398 \\}5410 \\}
5399 ,5411 , "tmp.zig:6:5: error: enumeration value 4 too large for type 'u2'");
5400 "tmp.zig:6:5: error: enumeration value 4 too large for type 'u2'"
5401 );
54025412
5403 cases.add(5413 cases.add(
5404 "specify non-integer enum tag type",5414 "specify non-integer enum tag type",
...@@ -5506,8 +5516,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -5506,8 +5516,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
5506 \\ var x = MultipleChoice.C;5516 \\ var x = MultipleChoice.C;
5507 \\}5517 \\}
5508 ,5518 ,
5509 "tmp.zig:6:9: error: enum tag value 60 already taken",5519 "tmp.zig:6:5: error: enum tag value 60 already taken",
5510 "tmp.zig:4:9: note: other occurrence here",5520 "tmp.zig:4:5: note: other occurrence here",
5511 );5521 );
55125522
5513 cases.add(5523 cases.add(
test/stage1/behavior/enum.zig+17-5
...@@ -940,13 +940,25 @@ test "enum literal in array literal" {...@@ -940,13 +940,25 @@ test "enum literal in array literal" {
940}940}
941941
942test "signed integer as enum tag" {942test "signed integer as enum tag" {
943 const SignedEnum = enum (i2) {943 const SignedEnum = enum(i2) {
944 A0 = -1,944 A0 = -1,
945 A1 = 0,945 A1 = 0,
946 A2 = 1,946 A2 = 1,
947 };947 };
948948
949 expect(@enumToInt(SignedEnum.A0) == -1);949 expect(@enumToInt(SignedEnum.A0) == -1);
950 expect(@enumToInt(SignedEnum.A1) == 0);950 expect(@enumToInt(SignedEnum.A1) == 0);
951 expect(@enumToInt(SignedEnum.A2) == 1);951 expect(@enumToInt(SignedEnum.A2) == 1);
952}
953
954test "enum value allocation" {
955 const LargeEnum = enum(u32) {
956 A0 = 0x80000000,
957 A1,
958 A2,
959 };
960
961 expect(@enumToInt(LargeEnum.A0) == 0x80000000);
962 expect(@enumToInt(LargeEnum.A1) == 0x80000001);
963 expect(@enumToInt(LargeEnum.A2) == 0x80000002);
952}964}