authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-11 20:38:13-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-05-11 20:38:13-04:00
logedcc7c72d1a684a8a16ca23ad26689f2cce4e803
treec8191ced7f0f35e70b5a824ab9f1245310c50f6b
parent6cf7fb1177848c2f2a20c1f4093b1e003c477911
parentd210628c91f2d33a168c71fc7633745ead925dfc
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #2459 from LemonBoy/enum-num-um-m

Signed types for enum tags

3 files changed, 102 insertions(+), 70 deletions(-)

src/analyze.cpp+61-47
...@@ -1908,6 +1908,18 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) {...@@ -1908,6 +1908,18 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) {
1908 return ErrorNone;1908 return ErrorNone;
1909}1909}
19101910
1911static bool type_is_valid_extern_enum_tag(CodeGen *g, ZigType *ty) {
1912 // Only integer types are allowed by the C ABI
1913 if(ty->id != ZigTypeIdInt)
1914 return false;
1915
1916 // According to the ANSI C standard the enumeration type should be either a
1917 // signed char, a signed integer or an unsigned one. But GCC/Clang allow
1918 // other integral types as a compiler extension so let's accomodate them
1919 // aswell.
1920 return type_allowed_in_extern(g, ty);
1921}
1922
1911static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {1923static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
1912 assert(enum_type->id == ZigTypeIdEnum);1924 assert(enum_type->id == ZigTypeIdEnum);
19131925
...@@ -1965,7 +1977,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -1965,7 +1977,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
1965 enum_type->abi_size = tag_int_type->abi_size;1977 enum_type->abi_size = tag_int_type->abi_size;
1966 enum_type->abi_align = tag_int_type->abi_align;1978 enum_type->abi_align = tag_int_type->abi_align;
19671979
1968 // TODO: Are extern enums allowed to have an init_arg_expr?
1969 if (decl_node->data.container_decl.init_arg_expr != nullptr) {1980 if (decl_node->data.container_decl.init_arg_expr != nullptr) {
1970 ZigType *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);1981 ZigType *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
1971 if (type_is_invalid(wanted_tag_int_type)) {1982 if (type_is_invalid(wanted_tag_int_type)) {
...@@ -1974,24 +1985,29 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -1974,24 +1985,29 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
1974 enum_type->data.enumeration.is_invalid = true;1985 enum_type->data.enumeration.is_invalid = true;
1975 add_node_error(g, decl_node->data.container_decl.init_arg_expr,1986 add_node_error(g, decl_node->data.container_decl.init_arg_expr,
1976 buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name)));1987 buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name)));
1977 } else if (wanted_tag_int_type->data.integral.is_signed) {1988 } else if (enum_type->data.enumeration.layout == ContainerLayoutExtern &&
1989 !type_is_valid_extern_enum_tag(g, wanted_tag_int_type)) {
1978 enum_type->data.enumeration.is_invalid = true;1990 enum_type->data.enumeration.is_invalid = true;
1979 add_node_error(g, decl_node->data.container_decl.init_arg_expr,1991 ErrorMsg *msg = add_node_error(g, decl_node->data.container_decl.init_arg_expr,
1980 buf_sprintf("expected unsigned integer, found '%s'", buf_ptr(&wanted_tag_int_type->name)));1992 buf_sprintf("'%s' is not a valid tag type for an extern enum",
1981 } else if (wanted_tag_int_type->data.integral.bit_count < tag_int_type->data.integral.bit_count) {1993 buf_ptr(&wanted_tag_int_type->name)));
1982 enum_type->data.enumeration.is_invalid = true;1994 add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr,
1983 add_node_error(g, decl_node->data.container_decl.init_arg_expr,1995 buf_sprintf("any integral type of size 8, 16, 32, 64 or 128 bit is valid"));
1984 buf_sprintf("'%s' too small to hold all bits; must be at least '%s'",
1985 buf_ptr(&wanted_tag_int_type->name), buf_ptr(&tag_int_type->name)));
1986 } else {1996 } else {
1987 tag_int_type = wanted_tag_int_type;1997 tag_int_type = wanted_tag_int_type;
1988 }1998 }
1989 }1999 }
2000
1990 enum_type->data.enumeration.tag_int_type = tag_int_type;2001 enum_type->data.enumeration.tag_int_type = tag_int_type;
1991 enum_type->size_in_bits = tag_int_type->size_in_bits;2002 enum_type->size_in_bits = tag_int_type->size_in_bits;
1992 enum_type->abi_size = tag_int_type->abi_size;2003 enum_type->abi_size = tag_int_type->abi_size;
1993 enum_type->abi_align = tag_int_type->abi_align;2004 enum_type->abi_align = tag_int_type->abi_align;
19942005
2006 BigInt bi_one;
2007 bigint_init_unsigned(&bi_one, 1);
2008
2009 TypeEnumField *last_enum_field = nullptr;
2010
1995 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) {
1996 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);
1997 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];2013 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
...@@ -2017,60 +2033,58 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {...@@ -2017,60 +2033,58 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
20172033
2018 AstNode *tag_value = field_node->data.struct_field.value;2034 AstNode *tag_value = field_node->data.struct_field.value;
20192035
2020 // In this first pass we resolve explicit tag values.
2021 // In a second pass we will fill in the unspecified ones.
2022 if (tag_value != nullptr) {2036 if (tag_value != nullptr) {
2037 // A user-specified value is available
2023 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);
2024 if (type_is_invalid(result->type)) {2039 if (type_is_invalid(result->type)) {
2025 enum_type->data.enumeration.is_invalid = true;2040 enum_type->data.enumeration.is_invalid = true;
2026 continue;2041 continue;
2027 }2042 }
2043
2028 assert(result->special != ConstValSpecialRuntime);2044 assert(result->special != ConstValSpecialRuntime);
2029 assert(result->type->id == ZigTypeIdInt ||2045 assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt);
2030 result->type->id == ZigTypeIdComptimeInt);2046
2031 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);
2032 if (entry == nullptr) {2048 } else {
2033 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);
2034 } else {2053 } else {
2035 Buf *val_buf = buf_alloc();2054 bigint_init_unsigned(&type_enum_field->value, 0);
2036 bigint_append_buf(val_buf, &result->data.x_bigint, 10);2055 }
20372056
2038 ErrorMsg *msg = add_node_error(g, tag_value,2057 // Make sure we can represent this number with tag_int_type
2039 buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));2058 if (!bigint_fits_in_bits(&type_enum_field->value,
2040 add_error_note(g, msg, entry->value,2059 tag_int_type->size_in_bits,
2041 buf_sprintf("other occurrence here"));2060 tag_int_type->data.integral.is_signed)) {
2042 enum_type->data.enumeration.is_invalid = true;2061 enum_type->data.enumeration.is_invalid = true;
2043 continue;2062
2063 Buf *val_buf = buf_alloc();
2064 bigint_append_buf(val_buf, &type_enum_field->value, 10);
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)));
2068
2069 break;
2044 }2070 }
2045 }2071 }
2046 }
20472072
2048 // Now iterate again and populate the unspecified tag values2073 // Make sure the value is unique
2049 uint32_t next_maybe_unoccupied_index = 0;2074 auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node);
2075 if (entry != nullptr) {
2076 enum_type->data.enumeration.is_invalid = true;
20502077
2051 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {2078 Buf *val_buf = buf_alloc();
2052 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);2079 bigint_append_buf(val_buf, &type_enum_field->value, 10);
2053 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
2054 AstNode *tag_value = field_node->data.struct_field.value;
20552080
2056 if (tag_value == nullptr) {2081 ErrorMsg *msg = add_node_error(g, field_node,
2057 if (occupied_tag_values.size() == 0) {2082 buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
2058 bigint_init_unsigned(&type_enum_field->value, next_maybe_unoccupied_index);2083 add_error_note(g, msg, entry->value,
2059 next_maybe_unoccupied_index += 1;2084 buf_sprintf("other occurrence here"));
2060 } else {
2061 BigInt proposed_value;
2062 for (;;) {
2063 bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index);
2064 next_maybe_unoccupied_index += 1;
2065 auto entry = occupied_tag_values.put_unique(proposed_value, field_node);
2066 if (entry != nullptr) {
2067 continue;
2068 }
2069 break;
2070 }
2071 bigint_init_bigint(&type_enum_field->value, &proposed_value);
2072 }
2073 }2085 }
2086
2087 last_enum_field = type_enum_field;
2074 }2088 }
20752089
2076 enum_type->data.enumeration.zero_bits_loop_flag = false;2090 enum_type->data.enumeration.zero_bits_loop_flag = false;
test/compile_errors.zig+17-23
...@@ -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:1:21: error: 'u2' too small to hold all bits; must be at least 'u3'",
5401 );
54025412
5403 cases.add(5413 cases.add(
5404 "specify non-integer enum tag type",5414 "specify non-integer enum tag type",
...@@ -5448,22 +5458,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -5448,22 +5458,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
5448 "tmp.zig:10:31: error: expected type 'u2', found 'u3'",5458 "tmp.zig:10:31: error: expected type 'u2', found 'u3'",
5449 );5459 );
54505460
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(5461 cases.add(
5468 "struct fields with value assignments",5462 "struct fields with value assignments",
5469 \\const MultipleChoice = struct {5463 \\const MultipleChoice = struct {
...@@ -5522,8 +5516,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -5522,8 +5516,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
5522 \\ var x = MultipleChoice.C;5516 \\ var x = MultipleChoice.C;
5523 \\}5517 \\}
5524 ,5518 ,
5525 "tmp.zig:6:9: error: enum tag value 60 already taken",5519 "tmp.zig:6:5: error: enum tag value 60 already taken",
5526 "tmp.zig:4:9: note: other occurrence here",5520 "tmp.zig:4:5: note: other occurrence here",
5527 );5521 );
55285522
5529 cases.add(5523 cases.add(
test/stage1/behavior/enum.zig+24
...@@ -938,3 +938,27 @@ test "enum literal in array literal" {...@@ -938,3 +938,27 @@ 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}
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);
964}