diff --git a/doc/langref.html.in b/doc/langref.html.in index b13427da90..6f12f0339f 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5483,6 +5483,10 @@ test "main" {

Converts an enumeration value into its integer tag type.

+

+ If the enum has only 1 possible value, the resut is a comptime_int + known at {#link|comptime#}. +

{#see_also|@intToEnum#} {#header_close#} diff --git a/src/analyze.cpp b/src/analyze.cpp index 59be76518b..78d907970e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -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(enum_type->data.enumeration.src_field_count); diff --git a/src/ir.cpp b/src/ir.cpp index 030f6627ad..3a94cdef38 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -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; diff --git a/test/cases/union.zig b/test/cases/union.zig index 0c55fb92e7..c93e769c5e 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -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); +}