fix tagged union with only 1 field tripping assertion

closes #1495

now the tag type of an enum with only 1 item is comptime_int.
This commit is contained in:
Andrew Kelley 2018-09-13 13:29:43 -04:00
parent 77fd147b26
commit 22e39e1e5a
No known key found for this signature in database
GPG Key ID: 4E7CD66038A4D47C
4 changed files with 79 additions and 2 deletions

View File

@ -5483,6 +5483,10 @@ test "main" {
<p>
Converts an enumeration value into its integer tag type.
</p>
<p>
If the enum has only 1 possible value, the resut is a <code class="zig">comptime_int</code>
known at {#link|comptime#}.
</p>
{#see_also|@intToEnum#}
{#header_close#}

View File

@ -2450,6 +2450,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
ZigType *tag_int_type;
if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
tag_int_type = get_c_int_type(g, CIntTypeInt);
} else if (enum_type->data.enumeration.layout == ContainerLayoutAuto && field_count == 1) {
tag_int_type = g->builtin_types.entry_num_lit_int;
} else {
tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
}
@ -2513,7 +2515,8 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
continue;
}
assert(result_inst->value.special != ConstValSpecialRuntime);
assert(result_inst->value.type->id == ZigTypeIdInt);
assert(result_inst->value.type->id == ZigTypeIdInt ||
result_inst->value.type->id == ZigTypeIdComptimeInt);
auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value);
if (entry == nullptr) {
bigint_init_bigint(&type_enum_field->value, &result_inst->value.data.x_bigint);
@ -2776,6 +2779,8 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
union_type->data.unionation.is_invalid = true;
return ErrorSemanticAnalyzeFail;
}
} else if (auto_layout && field_count == 1) {
tag_int_type = g->builtin_types.entry_num_lit_int;
} else {
tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
}
@ -2809,6 +2814,10 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));
return ErrorSemanticAnalyzeFail;
}
if ((err = type_ensure_zero_bits_known(g, enum_type))) {
assert(g->errors.length != 0);
return err;
}
tag_type = enum_type;
abi_alignment_so_far = get_abi_alignment(g, enum_type); // this populates src_field_count
covered_enum_fields = allocate<bool>(enum_type->data.enumeration.src_field_count);

View File

@ -10113,7 +10113,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour
IrInstruction *target, ZigType *wanted_type)
{
Error err;
assert(wanted_type->id == ZigTypeIdInt);
assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt);
ZigType *actual_type = target->value.type;
if ((err = ensure_complete_type(ira->codegen, actual_type)))
@ -10139,6 +10139,18 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour
return result;
}
// If there is only one possible tag, then we know at comptime what it is.
if (actual_type->data.enumeration.layout == ContainerLayoutAuto &&
actual_type->data.enumeration.src_field_count == 1)
{
assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int);
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type);
init_const_bigint(&result->value, wanted_type,
&actual_type->data.enumeration.fields[0].value);
return result;
}
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
source_instr->source_node, target);
result->value.type = wanted_type;
@ -10164,6 +10176,19 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou
return result;
}
// If there is only 1 possible tag, then we know at comptime what it is.
if (wanted_type->data.enumeration.layout == ContainerLayoutAuto &&
wanted_type->data.enumeration.src_field_count == 1)
{
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type);
result->value.special = ConstValSpecialStatic;
result->value.type = wanted_type;
TypeEnumField *enum_field = target->value.type->data.unionation.fields[0].enum_field;
bigint_init_bigint(&result->value.data.x_enum_tag, &enum_field->value);
return result;
}
IrInstruction *result = ir_build_union_tag(&ira->new_irb, source_instr->scope,
source_instr->source_node, target);
result->value.type = wanted_type;

View File

@ -324,3 +324,42 @@ test "tagged union with no payloads" {
@TagType(UnionEnumNoPayloads).B => {},
}
}
test "union with only 1 field casted to its enum type" {
const Literal = union(enum) {
Number: f64,
Bool: bool,
};
const Expr = union(enum) {
Literal: Literal,
};
var e = Expr{ .Literal = Literal{ .Bool = true } };
const Tag = @TagType(Expr);
comptime assert(@TagType(Tag) == comptime_int);
var t = Tag(e);
assert(t == Expr.Literal);
}
test "union with only 1 field casted to its enum type which has enum value specified" {
const Literal = union(enum) {
Number: f64,
Bool: bool,
};
const Tag = enum {
Literal = 33,
};
const Expr = union(Tag) {
Literal: Literal,
};
var e = Expr{ .Literal = Literal{ .Bool = true } };
comptime assert(@TagType(Tag) == comptime_int);
var t = Tag(e);
assert(t == Expr.Literal);
assert(@enumToInt(t) == 33);
comptime assert(@enumToInt(t) == 33);
}