diff --git a/src/analyze.cpp b/src/analyze.cpp index 3f50ccc2b9..d4e69a1fe6 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2005,15 +2005,6 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { buf_ptr(&wanted_tag_int_type->name))); add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr, buf_sprintf("valid types are 'i8', 'c_int' and 'c_uint' or compatible types")); - } else if (wanted_tag_int_type->data.integral.is_signed) { - enum_type->data.enumeration.is_invalid = true; - add_node_error(g, decl_node->data.container_decl.init_arg_expr, - buf_sprintf("expected unsigned integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); - } else if (wanted_tag_int_type->data.integral.bit_count < tag_int_type->data.integral.bit_count) { - enum_type->data.enumeration.is_invalid = true; - add_node_error(g, decl_node->data.container_decl.init_arg_expr, - buf_sprintf("'%s' too small to hold all bits; must be at least '%s'", - buf_ptr(&wanted_tag_int_type->name), buf_ptr(&tag_int_type->name))); } else { tag_int_type = wanted_tag_int_type; } @@ -2078,30 +2069,46 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { } // Now iterate again and populate the unspecified tag values - uint32_t next_maybe_unoccupied_index = 0; + BigInt next_maybe_unoccupied_index; + bigint_init_unsigned(&next_maybe_unoccupied_index, 0); + + // Since we're allocating positive values only we have one less bit + // available if the tag type is signed (eg. for a i8 we can only use (0,127)) + unsigned tag_bit_width = tag_int_type->size_in_bits; + if (tag_int_type->data.integral.is_signed) + tag_bit_width--; for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; AstNode *tag_value = field_node->data.struct_field.value; - if (tag_value == nullptr) { - if (occupied_tag_values.size() == 0) { - bigint_init_unsigned(&type_enum_field->value, next_maybe_unoccupied_index); - next_maybe_unoccupied_index += 1; - } else { - BigInt proposed_value; - for (;;) { - bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index); - next_maybe_unoccupied_index += 1; - auto entry = occupied_tag_values.put_unique(proposed_value, field_node); - if (entry != nullptr) { - continue; - } + // Already handled in the loop above + if (tag_value != nullptr) + continue; + + // Make sure we can represent this number with tag_int_type + const unsigned repr_bits = bigint_bits_needed(&next_maybe_unoccupied_index); + if (repr_bits > tag_bit_width) { + enum_type->data.enumeration.is_invalid = true; + add_node_error(g, field_node, + buf_sprintf("enumeration value %" ZIG_PRI_u64 " too large for type '%s'", + bigint_as_unsigned(&next_maybe_unoccupied_index), + buf_ptr(&tag_int_type->name))); + break; + } + + if (occupied_tag_values.size() == 0) { + type_enum_field->value = next_maybe_unoccupied_index; + bigint_incr(&next_maybe_unoccupied_index); + } else { + for (;;) { + auto entry = occupied_tag_values.put_unique(next_maybe_unoccupied_index, field_node); + if (entry == nullptr) break; - } - bigint_init_bigint(&type_enum_field->value, &proposed_value); + bigint_incr(&next_maybe_unoccupied_index); } + type_enum_field->value = next_maybe_unoccupied_index; } } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 6fc2fa65e7..eef5bc202c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -5397,7 +5397,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x = Small.One; \\} , - "tmp.zig:1:21: error: 'u2' too small to hold all bits; must be at least 'u3'", + "tmp.zig:6:5: error: enumeration value 4 too large for type 'u2'" ); cases.add( @@ -5448,22 +5448,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:10:31: error: expected type 'u2', found 'u3'", ); - cases.add( - "non unsigned integer enum tag type", - \\const Small = enum(i2) { - \\ One, - \\ Two, - \\ Three, - \\ Four, - \\}; - \\ - \\export fn entry() void { - \\ var y = Small.Two; - \\} - , - "tmp.zig:1:20: error: expected unsigned integer, found 'i2'", - ); - cases.add( "struct fields with value assignments", \\const MultipleChoice = struct { diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index 70515ec35b..5e49b6d491 100644 --- a/test/stage1/behavior/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -938,3 +938,15 @@ test "enum literal in array literal" { expect(array[0] == .one); expect(array[1] == .two); } + +test "signed integer as enum tag" { + const SignedEnum = enum (i2) { + A0 = -1, + A1 = 0, + A2 = 1, + }; + + expect(@enumToInt(SignedEnum.A0) == -1); + expect(@enumToInt(SignedEnum.A1) == 0); + expect(@enumToInt(SignedEnum.A2) == 1); +}