Commit 7a84fe79b9

Andrew Kelley <andrew@ziglang.org>
2019-02-18 18:56:17
pull request fixups
1 parent 9b3013d
Changed files (4)
doc
src
test
stage1
behavior
doc/langref.html.in
@@ -2221,8 +2221,9 @@ test "packed enum" {
       {#header_close#}
       {#header_open|union#}
       {#code_begin|test|union#}
-const assert = @import("std").debug.assert;
-const mem = @import("std").mem;
+const std = @import("std");
+const assert = std.debug.assert;
+const mem = std.mem;
 
 // A union has only 1 active field at a time.
 const Payload = union {
@@ -2231,16 +2232,19 @@ const Payload = union {
     Bool: bool,
 };
 test "simple union" {
-    var payload = Payload {.Int = 1234};
+    var payload = Payload{ .Int = 1234 };
     // payload.Float = 12.34; // ERROR! field not active
     assert(payload.Int == 1234);
     // You can activate another field by assigning the entire union.
-    payload = Payload {.Float = 12.34};
+    payload = Payload{ .Float = 12.34 };
     assert(payload.Float == 12.34);
 }
 
 // Unions can be given an enum tag type:
-const ComplexTypeTag = enum { Ok, NotOk }; 
+const ComplexTypeTag = enum {
+    Ok,
+    NotOk,
+};
 const ComplexType = union(ComplexTypeTag) {
     Ok: u8,
     NotOk: void,
@@ -2248,11 +2252,11 @@ const ComplexType = union(ComplexTypeTag) {
 
 // Declare a specific instance of the union variant.
 test "declare union value" {
-    const c = ComplexType { .Ok = 0 };
+    const c = ComplexType{ .Ok = 0 };
     assert(ComplexTypeTag(c) == ComplexTypeTag.Ok);
 }
 
-// @TagType can be used to access the enum tag type of a union.
+// @TagType can be used to access the enum tag type of a tagged union.
 test "@TagType" {
     assert(@TagType(ComplexType) == ComplexTypeTag);
 }
@@ -2266,7 +2270,7 @@ const Foo = union(enum) {
     None,
 };
 test "union variant switch" {
-    const p = Foo { .Number = 54 };
+    const p = Foo{ .Number = 54 };
     const what_is_it = switch (p) {
         // Capture by reference
         Foo.String => |*x| blk: {
@@ -2301,14 +2305,13 @@ const Variant = union(enum) {
 };
 
 test "union method" {
-    var v1 = Variant { .Int = 1 };
-    var v2 = Variant { .Bool = false };
+    var v1 = Variant{ .Int = 1 };
+    var v2 = Variant{ .Bool = false };
 
     assert(v1.truthy());
     assert(!v2.truthy());
 }
 
-
 const Small = union {
     A: i32,
     B: bool,
@@ -5660,12 +5663,13 @@ test "main" {
       {#header_close#}
 
       {#header_open|@enumToInt#}
-      <pre>{#syntax#}@enumToInt(enum_value: var) var{#endsyntax#}</pre>
+      <pre>{#syntax#}@enumToInt(enum_or_tagged_union: var) var{#endsyntax#}</pre>
       <p>
-      Converts an enumeration or tagged union value into its integer tag type.
+      Converts an enumeration value into its integer tag type. When a tagged union is passed,
+      the tag value is used as the enumeration value.
       </p>
       <p>
-      If the enum has only 1 possible value, the resut is a {#syntax#}comptime_int{#endsyntax#}
+      If there is only one possible enum value, the resut is a {#syntax#}comptime_int{#endsyntax#}
       known at {#link|comptime#}.
       </p>
       {#see_also|@intToEnum#}
src/ir.cpp
@@ -10305,63 +10305,75 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s
     return result;
 }
 
-static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr,
-        IrInstruction *target, ZigType *wanted_type)
-{
+static ZigType *ir_resolve_union_tag_type(IrAnalyze *ira, IrInstruction *source_instr, ZigType *union_type) {
+    assert(union_type->id == ZigTypeIdUnion);
+
     Error err;
-    assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt);
+    if ((err = type_resolve(ira->codegen, union_type, ResolveStatusSizeKnown)))
+        return ira->codegen->builtin_types.entry_invalid;
 
-    ZigType *actual_type = target->value.type;
+    AstNode *decl_node = union_type->data.unionation.decl_node;
+    if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) {
+        assert(union_type->data.unionation.tag_type != nullptr);
+        return union_type->data.unionation.tag_type;
+    } else {
+        ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union '%s' has no tag",
+            buf_ptr(&union_type->name)));
+        add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+}
 
-    if (actual_type->id == ZigTypeIdUnion)
-        actual_type = actual_type->data.unionation.tag_type;
+static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target) {
+    Error err;
 
-    if ((err = ensure_complete_type(ira->codegen, actual_type)))
+    IrInstruction *enum_target;
+    ZigType *enum_type;
+    if (target->value.type->id == ZigTypeIdUnion) {
+        enum_type = ir_resolve_union_tag_type(ira, target, target->value.type);
+        if (type_is_invalid(enum_type))
+            return ira->codegen->invalid_instruction;
+        enum_target = ir_implicit_cast(ira, target, enum_type);
+        if (type_is_invalid(enum_target->value.type))
+            return ira->codegen->invalid_instruction;
+    } else if (target->value.type->id == ZigTypeIdEnum) {
+        enum_target = target;
+        enum_type = target->value.type;
+    } else {
+        ir_add_error(ira, target,
+            buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name)));
         return ira->codegen->invalid_instruction;
+    }
 
-    if (wanted_type != actual_type->data.enumeration.tag_int_type) {
-        ir_add_error(ira, source_instr,
-                buf_sprintf("enum to integer cast to '%s' instead of its tag type, '%s'",
-                    buf_ptr(&wanted_type->name),
-                    buf_ptr(&actual_type->data.enumeration.tag_int_type->name)));
+    if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown)))
         return ira->codegen->invalid_instruction;
-    }
 
-    assert(actual_type->id == ZigTypeIdEnum);
+    ZigType *tag_type = enum_type->data.enumeration.tag_int_type;
+    assert(tag_type->id == ZigTypeIdInt || tag_type->id == ZigTypeIdComptimeInt);
 
-    if (instr_is_comptime(target)) {
-        ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
+    if (instr_is_comptime(enum_target)) {
+        ConstExprValue *val = ir_resolve_const(ira, enum_target, UndefBad);
         if (!val)
             return ira->codegen->invalid_instruction;
-        IrInstruction *result = ir_const(ira, source_instr, wanted_type);
-        if (target->value.type->id == ZigTypeIdUnion)
-            init_const_bigint(&result->value, wanted_type, &val->data.x_union.tag);
-        else
-            init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag);
-        
+        IrInstruction *result = ir_const(ira, source_instr, tag_type);
+        init_const_bigint(&result->value, tag_type, &val->data.x_enum_tag);
         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)
+    if (enum_type->data.enumeration.layout == ContainerLayoutAuto &&
+        enum_type->data.enumeration.src_field_count == 1)
     {
-        assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int);
-        IrInstruction *result = ir_const(ira, source_instr, wanted_type);
-        init_const_bigint(&result->value, wanted_type,
-            &actual_type->data.enumeration.fields[0].value);
-
+        assert(tag_type == ira->codegen->builtin_types.entry_num_lit_int);
+        IrInstruction *result = ir_const(ira, source_instr, tag_type);
+        init_const_bigint(&result->value, tag_type,
+                &enum_type->data.enumeration.fields[0].value);
         return result;
     }
 
-    IrInstruction *result = nullptr;
-    if (target->value.type->id == ZigTypeIdUnion)
-        result = ir_build_union_tag(&ira->new_irb, source_instr->scope,
-                    source_instr->source_node, target);
-    else
-        result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
-                    source_instr->source_node, target);
-    result->value.type = wanted_type;
+    IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
+            source_instr->source_node, enum_target);
+    result->value.type = tag_type;
     return result;
 }
 
@@ -21378,20 +21390,10 @@ static IrInstruction *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstruct
 
         return ir_const_type(ira, &instruction->base, enum_type->data.enumeration.tag_int_type);
     } else if (enum_type->id == ZigTypeIdUnion) {
-        if ((err = ensure_complete_type(ira->codegen, enum_type)))
-            return ira->codegen->invalid_instruction;
-
-        AstNode *decl_node = enum_type->data.unionation.decl_node;
-        if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) {
-            assert(enum_type->data.unionation.tag_type != nullptr);
-
-            return ir_const_type(ira, &instruction->base, enum_type->data.unionation.tag_type);
-        } else {
-            ErrorMsg *msg = ir_add_error(ira, target_inst, buf_sprintf("union '%s' has no tag",
-                buf_ptr(&enum_type->name)));
-            add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here"));
+        ZigType *tag_type = ir_resolve_union_tag_type(ira, instruction->target, enum_type);
+        if (type_is_invalid(tag_type))
             return ira->codegen->invalid_instruction;
-        }
+        return ir_const_type(ira, &instruction->base, tag_type);
     } else {
         ir_add_error(ira, target_inst, buf_sprintf("expected enum or union, found '%s'",
             buf_ptr(&enum_type->name)));
@@ -21972,38 +21974,11 @@ static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstr
 
 
 static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) {
-    Error err;
     IrInstruction *target = instruction->target->child;
-    ZigType *enum_type = target->value.type;
-    if (type_is_invalid(enum_type))
-        return ira->codegen->invalid_instruction;
-
-    if (enum_type->id == ZigTypeIdUnion) {
-        if ((err = ensure_complete_type(ira->codegen, enum_type)))
-            return ira->codegen->invalid_instruction;
-
-        AstNode *decl_node = enum_type->data.unionation.decl_node;
-        if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) {
-            assert(enum_type->data.unionation.tag_type != nullptr);
-            enum_type = target->value.type->data.unionation.tag_type;
-        } else {
-            ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("union '%s' has no tag",
-                buf_ptr(&enum_type->name)));
-            add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here"));
-            return ira->codegen->invalid_instruction;
-        }
-    } else if (enum_type->id != ZigTypeIdEnum) {
-        ir_add_error(ira, instruction->target,
-            buf_sprintf("expected enum or union(enum), found type '%s'", buf_ptr(&enum_type->name)));
-        return ira->codegen->invalid_instruction;
-    }
-
-    if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusZeroBitsKnown)))
+    if (type_is_invalid(target->value.type))
         return ira->codegen->invalid_instruction;
 
-    ZigType *int_type = enum_type->data.enumeration.tag_int_type;
-
-    return ir_analyze_enum_to_int(ira, &instruction->base, target, int_type);
+    return ir_analyze_enum_to_int(ira, &instruction->base, target);
 }
 
 static IrInstruction *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) {
test/stage1/behavior/union.zig
@@ -126,7 +126,7 @@ const MultipleChoice = union(enum(u32)) {
 test "simple union(enum(u32))" {
     var x = MultipleChoice.C;
     expect(x == MultipleChoice.C);
-    expect(@enumToInt(x) == 60);
+    expect(@enumToInt(@TagType(MultipleChoice)(x)) == 60);
 }
 
 const MultipleChoice2 = union(enum(u32)) {
@@ -148,7 +148,7 @@ test "union(enum(u32)) with specified and unspecified tag values" {
 }
 
 fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void {
-    expect(@enumToInt(x) == 60);
+    expect(@enumToInt(@TagType(MultipleChoice2)(x)) == 60);
     expect(1123 == switch (x) {
         MultipleChoice2.A => 1,
         MultipleChoice2.B => 2,
@@ -345,7 +345,23 @@ test "union with only 1 field casted to its enum type which has enum value speci
 
     var e = Expr{ .Literal = Literal{ .Bool = true } };
     comptime expect(@TagType(Tag) == comptime_int);
-    expect(Tag(e) == Expr.Literal);
-    expect(@enumToInt(e) == 33);
-    comptime expect(@enumToInt(e) == 33);
+    var t = Tag(e);
+    expect(t == Expr.Literal);
+    expect(@enumToInt(t) == 33);
+    comptime expect(@enumToInt(t) == 33);
+}
+
+test "@enumToInt works on unions" {
+    const Bar = union(enum) {
+        A: bool,
+        B: u8,
+        C,
+    };
+
+    const a = Bar{ .A = true };
+    var b = Bar{ .B = undefined };
+    var c = Bar.C;
+    expect(@enumToInt(a) == 0);
+    expect(@enumToInt(b) == 1);
+    expect(@enumToInt(c) == 2);
 }
test/tests.zig
@@ -572,9 +572,7 @@ pub const CompileErrorContext = struct {
             const source_file = ".tmp_source.zig";
 
             fn init(input: []const u8) ErrLineIter {
-                return ErrLineIter {
-                    .lines = mem.separate(input, "\n"),
-                };
+                return ErrLineIter{ .lines = mem.separate(input, "\n") };
             }
 
             fn next(self: *ErrLineIter) ?[]const u8 {
@@ -718,11 +716,10 @@ pub const CompileErrorContext = struct {
                 for (self.case.expected_errors.toSliceConst()) |expected| {
                     if (mem.indexOf(u8, stderr, expected) == null) {
                         warn(
-                            \\=========== Expected compile error: ============
+                            \\\n=========== Expected compile error: ============
                             \\{}
                             \\
-                        , expected
-                        );
+                        , expected);
                         ok = false;
                         break;
                     }
@@ -734,8 +731,7 @@ pub const CompileErrorContext = struct {
                     \\================= Full output: =================
                     \\{}
                     \\
-                , stderr
-                );
+                , stderr);
                 return error.TestFailed;
             }