Commit acdf1f0bde

Tadeo Kondrak <me@tadeo.ca>
2020-08-27 21:34:02
@Type for union fixes
1 parent 771f35c
Changed files (3)
src/analyze.cpp
@@ -2603,16 +2603,16 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
     if (decl_node->type == NodeTypeContainerDecl) {
         assert(!enum_type->data.enumeration.fields);
         field_count = (uint32_t)decl_node->data.container_decl.fields.length;
-        if (field_count == 0) {
-            add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
-
-            enum_type->data.enumeration.src_field_count = field_count;
-            enum_type->data.enumeration.fields = nullptr;
-            enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
-            return ErrorSemanticAnalyzeFail;
-        }
     } else {
-        field_count = enum_type->data.enumeration.src_field_count;
+        field_count = enum_type->data.enumeration.src_field_count + enum_type->data.enumeration.non_exhaustive;
+    }
+
+    if (field_count == 0) {
+        add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
+        enum_type->data.enumeration.src_field_count = field_count;
+        enum_type->data.enumeration.fields = nullptr;
+        enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
+        return ErrorSemanticAnalyzeFail;
     }
 
     Scope *scope = &enum_type->data.enumeration.decls_scope->base;
@@ -3072,18 +3072,19 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
     if (decl_node->type == NodeTypeContainerDecl) {
         assert(union_type->data.unionation.fields == nullptr);
         field_count = (uint32_t)decl_node->data.container_decl.fields.length;
-        if (field_count == 0) {
-            add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields"));
-            union_type->data.unionation.src_field_count = field_count;
-            union_type->data.unionation.resolve_status = ResolveStatusInvalid;
-            return ErrorSemanticAnalyzeFail;
-        }
         union_type->data.unionation.src_field_count = field_count;
         union_type->data.unionation.fields = heap::c_allocator.allocate<TypeUnionField>(field_count);
         union_type->data.unionation.fields_by_name.init(field_count);
     } else {
-        assert(union_type->data.unionation.fields != nullptr);
         field_count = union_type->data.unionation.src_field_count;
+        assert(field_count == 0 || union_type->data.unionation.fields != nullptr);
+    }
+
+    if (field_count == 0) {
+        add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields"));
+        union_type->data.unionation.src_field_count = field_count;
+        union_type->data.unionation.resolve_status = ResolveStatusInvalid;
+        return ErrorSemanticAnalyzeFail;
     }
 
     Scope *scope = &union_type->data.unionation.decls_scope->base;
@@ -3217,47 +3218,47 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
                 }
                 assert(field_type_val->special != ConstValSpecialRuntime);
                 union_field->type_val = field_type_val;
-                if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
-                    return ErrorSemanticAnalyzeFail;
+            }
 
-                bool field_is_opaque_type;
-                if ((err = type_val_resolve_is_opaque_type(g, field_type_val, &field_is_opaque_type))) {
-                    union_type->data.unionation.resolve_status = ResolveStatusInvalid;
-                    return ErrorSemanticAnalyzeFail;
-                }
-                if (field_is_opaque_type) {
-                    add_node_error(g, field_node,
-                        buf_create_from_str(
-                            "opaque types have unknown size and therefore cannot be directly embedded in unions"));
-                    union_type->data.unionation.resolve_status = ResolveStatusInvalid;
-                    return ErrorSemanticAnalyzeFail;
-                }
+            if (field_node->data.struct_field.value != nullptr && !is_auto_enum) {
+                ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
+                        buf_create_from_str("untagged union field assignment"));
+                add_error_note(g, msg, decl_node, buf_create_from_str("consider 'union(enum)' here"));
+            }
+        }
 
-                switch (type_val_resolve_requires_comptime(g, field_type_val)) {
-                    case ReqCompTimeInvalid:
-                        if (g->trace_err != nullptr) {
-                            g->trace_err = add_error_note(g, g->trace_err, field_node,
-                                buf_create_from_str("while checking this field"));
-                        }
-                        union_type->data.unionation.resolve_status = ResolveStatusInvalid;
-                        return ErrorSemanticAnalyzeFail;
-                    case ReqCompTimeYes:
-                        union_type->data.unionation.requires_comptime = true;
-                        break;
-                    case ReqCompTimeNo:
-                        break;
-                }
+        if (union_field->type_val != nullptr) {
+            bool field_is_opaque_type;
+            if ((err = type_val_resolve_is_opaque_type(g, union_field->type_val, &field_is_opaque_type))) {
+                union_type->data.unionation.resolve_status = ResolveStatusInvalid;
+                return ErrorSemanticAnalyzeFail;
+            }
+            if (field_is_opaque_type) {
+                add_node_error(g, union_field->decl_node,
+                    buf_create_from_str(
+                        "opaque types have unknown size and therefore cannot be directly embedded in unions"));
+                union_type->data.unionation.resolve_status = ResolveStatusInvalid;
+                return ErrorSemanticAnalyzeFail;
+            }
 
-                if ((err = type_val_resolve_zero_bits(g, field_type_val, union_type, nullptr, &is_zero_bits[i]))) {
+            switch (type_val_resolve_requires_comptime(g, union_field->type_val)) {
+                case ReqCompTimeInvalid:
+                    if (g->trace_err != nullptr) {
+                        g->trace_err = add_error_note(g, g->trace_err, union_field->decl_node,
+                            buf_create_from_str("while checking this field"));
+                    }
                     union_type->data.unionation.resolve_status = ResolveStatusInvalid;
                     return ErrorSemanticAnalyzeFail;
-                }
+                case ReqCompTimeYes:
+                    union_type->data.unionation.requires_comptime = true;
+                    break;
+                case ReqCompTimeNo:
+                    break;
             }
 
-            if (field_node->data.struct_field.value != nullptr && !is_auto_enum) {
-                ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
-                        buf_create_from_str("untagged union field assignment"));
-                add_error_note(g, msg, decl_node, buf_create_from_str("consider 'union(enum)' here"));
+            if ((err = type_val_resolve_zero_bits(g, union_field->type_val, union_type, nullptr, &is_zero_bits[i]))) {
+                union_type->data.unionation.resolve_status = ResolveStatusInvalid;
+                return ErrorSemanticAnalyzeFail;
             }
         }
 
src/ir.cpp
@@ -26222,14 +26222,19 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
             assert(payload->type == ir_type_info_get_type(ira, "Union", nullptr));
 
             ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0);
+            if (layout_value == nullptr)
+                return ira->codegen->invalid_inst_gen->value->type;
             assert(layout_value->special == ConstValSpecialStatic);
             assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr));
             ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag);
 
             ZigType *tag_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "tag_type", 1);
+            if (tag_type != nullptr && type_is_invalid(tag_type)) {
+                return ira->codegen->invalid_inst_gen->value->type;
+            }
             if (tag_type != nullptr && tag_type->id != ZigTypeIdEnum) {
                 ir_add_error(ira, source_instr, buf_sprintf(
-                    "union tag type must be an enum, not %s", type_id_name(tag_type->id)));
+                    "expected enum type, found '%s'", type_id_name(tag_type->id)));
                 return ira->codegen->invalid_inst_gen->value->type;
             }
 
test/compile_errors.zig
@@ -10,6 +10,63 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         "tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8'",
     });
 
+    cases.add("@Type for union with opaque field",
+        \\const TypeInfo = @import("builtin").TypeInfo;
+        \\const Untagged = @Type(.{
+        \\    .Union = .{
+        \\        .layout = .Auto,
+        \\        .tag_type = null,
+        \\        .fields = &[_]TypeInfo.UnionField{
+        \\            .{ .name = "foo", .field_type = @Type(.Opaque) },
+        \\        },
+        \\        .decls = &[_]TypeInfo.Declaration{},
+        \\    },
+        \\});
+        \\export fn entry() void {
+        \\    _ = Untagged{};
+        \\}
+    , &[_][]const u8{
+        "tmp.zig:2:25: error: opaque types have unknown size and therefore cannot be directly embedded in unions",
+        "tmp.zig:13:17: note: referenced here",
+    });
+
+    cases.add("@Type for union with zero fields",
+        \\const TypeInfo = @import("builtin").TypeInfo;
+        \\const Untagged = @Type(.{
+        \\    .Union = .{
+        \\        .layout = .Auto,
+        \\        .tag_type = null,
+        \\        .fields = &[_]TypeInfo.UnionField{},
+        \\        .decls = &[_]TypeInfo.Declaration{},
+        \\    },
+        \\});
+        \\export fn entry() void {
+        \\    _ = Untagged{};
+        \\}
+    , &[_][]const u8{
+        "tmp.zig:2:25: error: unions must have 1 or more fields",
+        "tmp.zig:11:17: note: referenced here",
+    });
+
+    cases.add("@Type for exhaustive enum with zero fields",
+        \\const TypeInfo = @import("builtin").TypeInfo;
+        \\const Tag = @Type(.{
+        \\    .Enum = .{
+        \\        .layout = .Auto,
+        \\        .tag_type = u1,
+        \\        .fields = &[_]TypeInfo.EnumField{},
+        \\        .decls = &[_]TypeInfo.Declaration{},
+        \\        .is_exhaustive = true,
+        \\    },
+        \\});
+        \\export fn entry() void {
+        \\    _ = @intToEnum(Tag, 0);
+        \\}
+    , &[_][]const u8{
+        "tmp.zig:2:20: error: enums must have 1 or more fields",
+        "tmp.zig:12:9: note: referenced here",
+    });
+
     cases.add("@Type for tagged union with extra union field",
         \\const TypeInfo = @import("builtin").TypeInfo;
         \\const Tag = @Type(.{