Commit 22e39e1e5a

Andrew Kelley <superjoe30@gmail.com>
2018-09-13 19:29:43
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.
1 parent 77fd147
Changed files (4)
doc/langref.html.in
@@ -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#}
 
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<bool>(enum_type->data.enumeration.src_field_count);
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;
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);
+}