Commit 25a5fc32fe

Andrew Kelley <superjoe30@gmail.com>
2016-12-28 07:15:09
IR: pass passSliceOfEmptyStructToFn test
1 parent 15f843e
src/all_types.hpp
@@ -235,6 +235,7 @@ struct TypeEnumField {
     Buf *name;
     TypeTableEntry *type_entry;
     uint32_t value;
+    uint32_t gen_index;
 };
 
 enum NodeType {
@@ -813,6 +814,9 @@ struct TypeTableEntryStruct {
     bool reported_infinite_err;
     // whether we've finished resolving it
     bool complete;
+
+    bool zero_bits_loop_flag;
+    bool zero_bits_known;
 };
 
 struct TypeTableEntryMaybe {
@@ -840,6 +844,9 @@ struct TypeTableEntryEnum {
     bool reported_infinite_err;
     // whether we've finished resolving it
     bool complete;
+
+    bool zero_bits_loop_flag;
+    bool zero_bits_known;
 };
 
 struct TypeTableEntryEnumTag {
@@ -862,6 +869,9 @@ struct TypeTableEntryUnion {
     bool reported_infinite_err;
     // whether we've finished resolving it
     bool complete;
+
+    bool zero_bits_loop_flag;
+    bool zero_bits_known;
 };
 
 struct FnGenParamInfo {
src/analyze.cpp
@@ -20,6 +20,10 @@ static const size_t default_backward_branch_quota = 1000;
 static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type);
 static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type);
 
+static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type);
+static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type);
+static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type);
+
 AstNode *first_executing_node(AstNode *node) {
     switch (node->type) {
         case NodeTypeFnCallExpr:
@@ -257,6 +261,44 @@ bool type_is_complete(TypeTableEntry *type_entry) {
     zig_unreachable();
 }
 
+bool type_has_zero_bits_known(TypeTableEntry *type_entry) {
+    switch (type_entry->id) {
+        case TypeTableEntryIdInvalid:
+        case TypeTableEntryIdVar:
+            zig_unreachable();
+        case TypeTableEntryIdStruct:
+            return type_entry->data.structure.zero_bits_known;
+        case TypeTableEntryIdEnum:
+            return type_entry->data.enumeration.zero_bits_known;
+        case TypeTableEntryIdUnion:
+            return type_entry->data.unionation.zero_bits_known;
+        case TypeTableEntryIdMetaType:
+        case TypeTableEntryIdVoid:
+        case TypeTableEntryIdBool:
+        case TypeTableEntryIdUnreachable:
+        case TypeTableEntryIdInt:
+        case TypeTableEntryIdFloat:
+        case TypeTableEntryIdPointer:
+        case TypeTableEntryIdArray:
+        case TypeTableEntryIdNumLitFloat:
+        case TypeTableEntryIdNumLitInt:
+        case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
+        case TypeTableEntryIdMaybe:
+        case TypeTableEntryIdErrorUnion:
+        case TypeTableEntryIdPureError:
+        case TypeTableEntryIdFn:
+        case TypeTableEntryIdTypeDecl:
+        case TypeTableEntryIdNamespace:
+        case TypeTableEntryIdBlock:
+        case TypeTableEntryIdBoundFn:
+        case TypeTableEntryIdEnumTag:
+            return true;
+    }
+    zig_unreachable();
+}
+
+
 uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry) {
     if (type_has_bits(type_entry)) {
         return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref);
@@ -475,13 +517,14 @@ TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t
         return entry;
     } else {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
-        entry->type_ref = child_type->type_ref ? LLVMArrayType(child_type->type_ref, array_size) : nullptr;
         entry->zero_bits = (array_size == 0) || child_type->zero_bits;
 
         buf_resize(&entry->name, 0);
         buf_appendf(&entry->name, "[%" PRIu64 "]%s", array_size, buf_ptr(&child_type->name));
 
         if (!entry->zero_bits) {
+            entry->type_ref = child_type->type_ref ? LLVMArrayType(child_type->type_ref, array_size) : nullptr;
+
             uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
             uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref);
 
@@ -507,14 +550,21 @@ static void slice_type_common_init(CodeGen *g, TypeTableEntry *child_type,
     entry->data.structure.src_field_count = element_count;
     entry->data.structure.gen_field_count = element_count;
     entry->data.structure.fields = allocate<TypeStructField>(element_count);
-    entry->data.structure.fields[0].name = buf_create_from_str("ptr");
-    entry->data.structure.fields[0].type_entry = pointer_type;
-    entry->data.structure.fields[0].src_index = 0;
-    entry->data.structure.fields[0].gen_index = 0;
-    entry->data.structure.fields[1].name = buf_create_from_str("len");
-    entry->data.structure.fields[1].type_entry = g->builtin_types.entry_usize;
-    entry->data.structure.fields[1].src_index = 1;
-    entry->data.structure.fields[1].gen_index = 1;
+    entry->data.structure.fields[slice_ptr_index].name = buf_create_from_str("ptr");
+    entry->data.structure.fields[slice_ptr_index].type_entry = pointer_type;
+    entry->data.structure.fields[slice_ptr_index].src_index = slice_ptr_index;
+    entry->data.structure.fields[slice_ptr_index].gen_index = 0;
+    entry->data.structure.fields[slice_len_index].name = buf_create_from_str("len");
+    entry->data.structure.fields[slice_len_index].type_entry = g->builtin_types.entry_usize;
+    entry->data.structure.fields[slice_len_index].src_index = slice_len_index;
+    entry->data.structure.fields[slice_len_index].gen_index = 1;
+
+    assert(type_has_zero_bits_known(child_type));
+    if (child_type->zero_bits) {
+        entry->data.structure.gen_field_count = 1;
+        entry->data.structure.fields[slice_ptr_index].gen_index = SIZE_MAX;
+        entry->data.structure.fields[slice_len_index].gen_index = 0;
+    }
 }
 
 TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) {
@@ -535,6 +585,7 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c
         entry->type_ref = var_peer->type_ref;
         entry->di_type = var_peer->di_type;
         entry->data.structure.complete = true;
+        entry->data.structure.zero_bits_known = true;
 
         *parent_pointer = entry;
         return entry;
@@ -544,7 +595,7 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c
         // If the child type is []const T then we need to make sure the type ref
         // and debug info is the same as if the child type were []T.
         if (is_slice(child_type)) {
-            TypeTableEntry *ptr_type = child_type->data.structure.fields[0].type_entry;
+            TypeTableEntry *ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry;
             assert(ptr_type->id == TypeTableEntryIdPointer);
             if (ptr_type->data.pointer.is_const) {
                 TypeTableEntry *non_const_child_type = get_slice_type(g,
@@ -560,11 +611,6 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c
         buf_appendf(&entry->name, "[]%s", buf_ptr(&child_type->name));
 
         slice_type_common_init(g, child_type, is_const, entry);
-        if (child_type->zero_bits) {
-            entry->data.structure.gen_field_count = 1;
-            entry->data.structure.fields[0].gen_index = SIZE_MAX;
-            entry->data.structure.fields[1].gen_index = 0;
-        }
 
         if (!entry->type_ref) {
             entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&entry->name));
@@ -582,12 +628,6 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c
                 };
                 LLVMStructSetBody(entry->type_ref, element_types, 1, false);
 
-                slice_type_common_init(g, child_type, is_const, entry);
-
-                entry->data.structure.gen_field_count = 1;
-                entry->data.structure.fields[0].gen_index = -1;
-                entry->data.structure.fields[1].gen_index = 0;
-
                 TypeTableEntry *usize_type = g->builtin_types.entry_usize;
                 uint64_t len_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, usize_type->type_ref);
                 uint64_t len_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, usize_type->type_ref);
@@ -622,8 +662,6 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c
                 };
                 LLVMStructSetBody(entry->type_ref, element_types, element_count, false);
 
-                slice_type_common_init(g, child_type, is_const, entry);
-
 
                 uint64_t ptr_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, pointer_type->type_ref);
                 uint64_t ptr_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, pointer_type->type_ref);
@@ -664,6 +702,7 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c
 
 
         entry->data.structure.complete = true;
+        entry->data.structure.zero_bits_known = true;
 
         *parent_pointer = entry;
         return entry;
@@ -706,6 +745,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
     if (table_entry) {
         return table_entry->value;
     }
+    ensure_complete_type(g, fn_type_id->return_type);
 
     TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
     fn_type->data.fn.fn_type_id = *fn_type_id;
@@ -750,7 +790,6 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
     // next, loop over the parameters again and compute debug information
     // and codegen information
     if (!skip_debug_info) {
-        ensure_complete_type(g, fn_type_id->return_type);
         bool first_arg_return = !fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type);
         // +1 for maybe making the first argument the return value
         LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id->param_count);
@@ -1056,33 +1095,31 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
     // if you change this logic you likely must also change similar logic in parseh.cpp
     assert(enum_type->id == TypeTableEntryIdEnum);
 
+    resolve_enum_zero_bits(g, enum_type);
+    if (enum_type->data.enumeration.is_invalid)
+        return;
+
     AstNode *decl_node = enum_type->data.enumeration.decl_node;
 
     if (enum_type->data.enumeration.embedded_in_current) {
         if (!enum_type->data.enumeration.reported_infinite_err) {
             enum_type->data.enumeration.reported_infinite_err = true;
-            add_node_error(g, decl_node, buf_sprintf("enum has infinite size"));
+            add_node_error(g, decl_node, buf_sprintf("enum contains itself"));
         }
         return;
     }
 
-    if (enum_type->data.enumeration.fields) {
-        // we already resolved this type. skip
-        return;
-    }
-
+    assert(!enum_type->data.enumeration.zero_bits_loop_flag);
     assert(decl_node->type == NodeTypeContainerDecl);
     assert(enum_type->di_type);
 
-    uint32_t field_count = decl_node->data.container_decl.fields.length;
+    uint32_t field_count = enum_type->data.enumeration.src_field_count;
 
-    enum_type->data.enumeration.src_field_count = field_count;
-    enum_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
+    assert(enum_type->data.enumeration.fields);
     ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count);
 
-    // we possibly allocate too much here since gen_field_count can be lower than field_count.
-    // the only problem is potential wasted space though.
-    ZigLLVMDIType **union_inner_di_types = allocate<ZigLLVMDIType*>(field_count);
+    uint32_t gen_field_count = enum_type->data.enumeration.gen_field_count;
+    ZigLLVMDIType **union_inner_di_types = allocate<ZigLLVMDIType*>(gen_field_count);
 
     TypeTableEntry *biggest_union_member = nullptr;
     uint64_t biggest_align_in_bits = 0;
@@ -1094,14 +1131,10 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
     // set temporary flag
     enum_type->data.enumeration.embedded_in_current = true;
 
-    size_t gen_field_index = 0;
     for (uint32_t i = 0; i < field_count; i += 1) {
         AstNode *field_node = decl_node->data.container_decl.fields.at(i);
         TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i];
-        type_enum_field->name = field_node->data.struct_field.name;
-        TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
-        type_enum_field->type_entry = field_type;
-        type_enum_field->value = i;
+        TypeTableEntry *field_type = type_enum_field->type_entry;
 
         di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i);
 
@@ -1120,7 +1153,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
         assert(debug_size_in_bits > 0);
         assert(debug_align_in_bits > 0);
 
-        union_inner_di_types[gen_field_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
+        union_inner_di_types[type_enum_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
                 ZigLLVMTypeToScope(enum_type->di_type), buf_ptr(type_enum_field->name),
                 import->di_file, field_node->line + 1,
                 debug_size_in_bits,
@@ -1136,8 +1169,6 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
             biggest_union_member = field_type;
             biggest_union_member_size_in_bits = debug_size_in_bits;
         }
-
-        gen_field_index += 1;
     }
 
     // unset temporary flag
@@ -1145,7 +1176,6 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
     enum_type->data.enumeration.complete = true;
 
     if (!enum_type->data.enumeration.is_invalid) {
-        enum_type->data.enumeration.gen_field_count = gen_field_index;
         enum_type->data.enumeration.union_type = biggest_union_member;
 
         TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count);
@@ -1176,7 +1206,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
             ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
                     ZigLLVMTypeToScope(enum_type->di_type), "AnonUnion", import->di_file, decl_node->line + 1,
                     biggest_union_member_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types,
-                    gen_field_index, 0, "");
+                    gen_field_count, 0, "");
 
             // create debug types for members of root struct
             uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, 0);
@@ -1233,9 +1263,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
 
             ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type);
             enum_type->di_type = tag_di_type;
-
         }
-
     }
 }
 
@@ -1244,51 +1272,38 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
     // parseh.cpp
     assert(struct_type->id == TypeTableEntryIdStruct);
 
+    resolve_struct_zero_bits(g, struct_type);
+    if (struct_type->data.structure.is_invalid)
+        return;
+
     AstNode *decl_node = struct_type->data.structure.decl_node;
 
     if (struct_type->data.structure.embedded_in_current) {
         struct_type->data.structure.is_invalid = true;
         if (!struct_type->data.structure.reported_infinite_err) {
             struct_type->data.structure.reported_infinite_err = true;
-            add_node_error(g, decl_node,
-                    buf_sprintf("struct has infinite size"));
+            add_node_error(g, decl_node, buf_sprintf("struct contains itself"));
         }
         return;
     }
 
-    if (struct_type->data.structure.fields) {
-        // we already resolved this type. skip
-        return;
-    }
-
+    assert(!struct_type->data.structure.zero_bits_loop_flag);
+    assert(struct_type->data.enumeration.fields);
     assert(decl_node->type == NodeTypeContainerDecl);
-    assert(struct_type->di_type);
-
-    size_t field_count = decl_node->data.container_decl.fields.length;
 
-    struct_type->data.structure.src_field_count = field_count;
-    struct_type->data.structure.fields = allocate<TypeStructField>(field_count);
+    size_t field_count = struct_type->data.structure.src_field_count;
 
-    // we possibly allocate too much here since gen_field_count can be lower than field_count.
-    // the only problem is potential wasted space though.
-    LLVMTypeRef *element_types = allocate<LLVMTypeRef>(field_count);
+    size_t gen_field_count = struct_type->data.structure.gen_field_count;
+    LLVMTypeRef *element_types = allocate<LLVMTypeRef>(gen_field_count);
 
     // this field should be set to true only during the recursive calls to resolve_struct_type
     struct_type->data.structure.embedded_in_current = true;
 
     Scope *scope = &struct_type->data.structure.decls_scope->base;
-    ImportTableEntry *import = get_scope_import(scope);
 
-    size_t gen_field_index = 0;
     for (size_t i = 0; i < field_count; i += 1) {
-        AstNode *field_node = decl_node->data.container_decl.fields.at(i);
         TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
-        type_struct_field->name = field_node->data.struct_field.name;
-        TypeTableEntry *field_type = analyze_type_expr(g, scope,
-                field_node->data.struct_field.type);
-        type_struct_field->type_entry = field_type;
-        type_struct_field->src_index = i;
-        type_struct_field->gen_index = SIZE_MAX;
+        TypeTableEntry *field_type = type_struct_field->type_entry;
 
         ensure_complete_type(g, field_type);
         if (field_type->id == TypeTableEntryIdInvalid) {
@@ -1299,31 +1314,31 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
         if (!type_has_bits(field_type))
             continue;
 
-        type_struct_field->gen_index = gen_field_index;
-
-        element_types[gen_field_index] = field_type->type_ref;
-        assert(element_types[gen_field_index]);
-
-        gen_field_index += 1;
+        element_types[type_struct_field->gen_index] = field_type->type_ref;
+        assert(element_types[type_struct_field->gen_index]);
     }
     struct_type->data.structure.embedded_in_current = false;
-
-    struct_type->data.structure.gen_field_count = gen_field_index;
     struct_type->data.structure.complete = true;
 
-    if (struct_type->data.structure.is_invalid) {
+    if (struct_type->data.structure.is_invalid)
+        return;
+
+    if (struct_type->zero_bits) {
+        struct_type->type_ref = LLVMVoidType();
+        struct_type->di_type = g->builtin_types.entry_void->di_type;
         return;
     }
+    assert(struct_type->di_type);
 
-    size_t gen_field_count = gen_field_index;
     LLVMStructSetBody(struct_type->type_ref, element_types, gen_field_count, false);
 
     ZigLLVMDIType **di_element_types = allocate<ZigLLVMDIType*>(gen_field_count);
 
+    ImportTableEntry *import = get_scope_import(scope);
     for (size_t i = 0; i < field_count; i += 1) {
         AstNode *field_node = decl_node->data.container_decl.fields.at(i);
         TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
-        gen_field_index = type_struct_field->gen_index;
+        size_t gen_field_index = type_struct_field->gen_index;
         if (gen_field_index == SIZE_MAX) {
             continue;
         }
@@ -1362,13 +1377,122 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
     ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type);
     struct_type->di_type = replacement_di_type;
 
-    struct_type->zero_bits = (debug_size_in_bits == 0);
+    assert((debug_size_in_bits == 0) == struct_type->zero_bits);
 }
 
 static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
     zig_panic("TODO");
 }
 
+static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
+    assert(enum_type->id == TypeTableEntryIdEnum);
+
+    if (enum_type->data.enumeration.zero_bits_known)
+        return;
+
+    if (enum_type->data.enumeration.zero_bits_loop_flag) {
+        enum_type->data.enumeration.zero_bits_known = true;
+        return;
+    }
+
+    enum_type->data.enumeration.zero_bits_loop_flag = true;
+
+    AstNode *decl_node = enum_type->data.enumeration.decl_node;
+    assert(decl_node->type == NodeTypeContainerDecl);
+    assert(enum_type->di_type);
+
+    assert(!enum_type->data.enumeration.fields);
+    uint32_t field_count = decl_node->data.container_decl.fields.length;
+    enum_type->data.enumeration.src_field_count = field_count;
+    enum_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
+
+    Scope *scope = &enum_type->data.enumeration.decls_scope->base;
+
+    uint32_t gen_field_index = 0;
+    for (uint32_t i = 0; i < field_count; i += 1) {
+        AstNode *field_node = decl_node->data.container_decl.fields.at(i);
+        TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i];
+        type_enum_field->name = field_node->data.struct_field.name;
+        TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
+        type_enum_field->type_entry = field_type;
+        type_enum_field->value = i;
+
+        type_ensure_zero_bits_known(g, field_type);
+        if (field_type->id == TypeTableEntryIdInvalid) {
+            enum_type->data.enumeration.is_invalid = true;
+            continue;
+        }
+
+        if (!type_has_bits(field_type))
+            continue;
+
+        type_enum_field->gen_index = gen_field_index;
+        gen_field_index += 1;
+    }
+
+    enum_type->data.enumeration.zero_bits_loop_flag = false;
+    enum_type->data.enumeration.gen_field_count = gen_field_index;
+    enum_type->zero_bits = (gen_field_index == 0 && field_count < 2);
+    enum_type->data.enumeration.zero_bits_known = true;
+}
+
+static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
+    assert(struct_type->id == TypeTableEntryIdStruct);
+
+    if (struct_type->data.structure.zero_bits_known)
+        return;
+
+    if (struct_type->data.structure.zero_bits_loop_flag) {
+        struct_type->data.structure.zero_bits_known = true;
+        return;
+    }
+
+    struct_type->data.structure.zero_bits_loop_flag = true;
+
+    AstNode *decl_node = struct_type->data.structure.decl_node;
+    assert(decl_node->type == NodeTypeContainerDecl);
+    assert(struct_type->di_type);
+
+    assert(!struct_type->data.structure.fields);
+    size_t field_count = decl_node->data.container_decl.fields.length;
+    struct_type->data.structure.src_field_count = field_count;
+    struct_type->data.structure.fields = allocate<TypeStructField>(field_count);
+
+    Scope *scope = &struct_type->data.structure.decls_scope->base;
+
+    size_t gen_field_index = 0;
+    for (size_t i = 0; i < field_count; i += 1) {
+        AstNode *field_node = decl_node->data.container_decl.fields.at(i);
+        TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
+        type_struct_field->name = field_node->data.struct_field.name;
+        TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
+        type_struct_field->type_entry = field_type;
+        type_struct_field->src_index = i;
+        type_struct_field->gen_index = SIZE_MAX;
+
+        type_ensure_zero_bits_known(g, field_type);
+        if (field_type->id == TypeTableEntryIdInvalid) {
+            struct_type->data.structure.is_invalid = true;
+            continue;
+        }
+
+        if (!type_has_bits(field_type))
+            continue;
+
+        type_struct_field->gen_index = gen_field_index;
+        gen_field_index += 1;
+    }
+
+    struct_type->data.structure.zero_bits_loop_flag = false;
+    struct_type->data.structure.gen_field_count = gen_field_index;
+    struct_type->zero_bits = (gen_field_index == 0);
+    struct_type->data.structure.zero_bits_known = true;
+}
+
+static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
+    zig_panic("TODO resolve_union_zero_bits");
+}
+
 static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) {
     if (!scope)
         return;
@@ -3064,6 +3188,16 @@ void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry) {
     }
 }
 
+void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry) {
+    if (type_entry->id == TypeTableEntryIdStruct) {
+        resolve_struct_zero_bits(g, type_entry);
+    } else if (type_entry->id == TypeTableEntryIdEnum) {
+        resolve_enum_zero_bits(g, type_entry);
+    } else if (type_entry->id == TypeTableEntryIdUnion) {
+        resolve_union_zero_bits(g, type_entry);
+    }
+}
+
 bool ir_get_var_is_comptime(VariableTableEntry *var) {
     if (!var->is_comptime)
         return false;
src/analyze.hpp
@@ -54,6 +54,7 @@ bool type_is_codegen_pointer(TypeTableEntry *type);
 TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry);
 TypeTableEntry *container_ref_type(TypeTableEntry *type_entry);
 bool type_is_complete(TypeTableEntry *type_entry);
+bool type_has_zero_bits_known(TypeTableEntry *type_entry);
 void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry);
 TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name);
 ScopeDecls *get_container_scope(TypeTableEntry *type_entry);
@@ -75,6 +76,7 @@ AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index);
 FnTableEntry *scope_get_fn_if_root(Scope *scope);
 bool type_requires_comptime(TypeTableEntry *type_entry);
 void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry);
+void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry);
 void complete_enum(CodeGen *g, TypeTableEntry *enum_type);
 bool ir_get_var_is_comptime(VariableTableEntry *var);
 bool const_values_equal(ConstExprValue *a, ConstExprValue *b);
src/codegen.cpp
@@ -2404,6 +2404,7 @@ static void ir_render(CodeGen *g, FnTableEntry *fn_entry) {
 
 static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
     TypeTableEntry *canon_type = get_underlying_type(const_val->type);
+    assert(!canon_type->zero_bits);
 
     switch (const_val->special) {
         case ConstValSpecialRuntime:
@@ -2557,6 +2558,14 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
                 } else {
                     ConstExprValue *array_const_val = const_val->data.x_ptr.base_ptr;
                     assert(array_const_val->type->id == TypeTableEntryIdArray);
+                    if (array_const_val->type->zero_bits) {
+                        // make this a null pointer
+                        TypeTableEntry *usize_type = g->builtin_types.entry_usize;
+                        const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize_type->type_ref),
+                                const_val->type->type_ref);
+                        render_const_val_global(g, const_val);
+                        return const_val->llvm_value;
+                    }
                     render_const_val(g, array_const_val);
                     render_const_val_global(g, array_const_val);
                     TypeTableEntry *usize = g->builtin_types.entry_usize;
@@ -3369,6 +3378,7 @@ static void define_builtin_types(CodeGen *g) {
             }
         }
         entry->data.enumeration.complete = true;
+        entry->data.enumeration.zero_bits_known = true;
 
         TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);
         entry->data.enumeration.tag_type = tag_type_entry;
@@ -3402,6 +3412,7 @@ static void define_builtin_types(CodeGen *g) {
             }
         }
         entry->data.enumeration.complete = true;
+        entry->data.enumeration.zero_bits_known = true;
 
         TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);
         entry->data.enumeration.tag_type = tag_type_entry;
@@ -3429,6 +3440,7 @@ static void define_builtin_types(CodeGen *g) {
             }
         }
         entry->data.enumeration.complete = true;
+        entry->data.enumeration.zero_bits_known = true;
 
         TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);
         entry->data.enumeration.tag_type = tag_type_entry;
@@ -3456,6 +3468,7 @@ static void define_builtin_types(CodeGen *g) {
             }
         }
         entry->data.enumeration.complete = true;
+        entry->data.enumeration.zero_bits_known = true;
 
         TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);
         entry->data.enumeration.tag_type = tag_type_entry;
@@ -3491,6 +3504,7 @@ static void define_builtin_types(CodeGen *g) {
         entry->data.enumeration.fields[5].type_entry = g->builtin_types.entry_void;
 
         entry->data.enumeration.complete = true;
+        entry->data.enumeration.zero_bits_known = true;
 
         TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);
         entry->data.enumeration.tag_type = tag_type_entry;
src/ir.cpp
@@ -7941,6 +7941,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
         case TypeTableEntryIdBoundFn:
         case TypeTableEntryIdEnumTag:
             {
+                type_ensure_zero_bits_known(ira->codegen, resolved_child_type);
                 TypeTableEntry *result_type = get_slice_type(ira->codegen, resolved_child_type, is_const);
                 ConstExprValue *out_val = ir_build_const_from(ira, &slice_type_instruction->base,
                         child_type->value.depends_on_compile_var);
src/parseh.cpp
@@ -717,6 +717,7 @@ static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl)
 
         enum_type->data.enumeration.gen_field_count = 0;
         enum_type->data.enumeration.complete = true;
+        enum_type->data.enumeration.zero_bits_known = true;
         enum_type->data.enumeration.tag_type = tag_type_entry;
 
         enum_type->data.enumeration.src_field_count = field_count;
@@ -937,6 +938,7 @@ static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_
 
     struct_type->data.structure.gen_field_count = field_count;
     struct_type->data.structure.complete = true;
+    struct_type->data.structure.zero_bits_known = true;
 
     uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(c->codegen->target_data_ref, struct_type->type_ref);
     uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(c->codegen->target_data_ref, struct_type->type_ref);
test/cases/struct.zig
@@ -198,6 +198,14 @@ fn testReturnEmptyStructFromFn() -> EmptyStruct2 {
     EmptyStruct2 {}
 }
 
+fn passSliceOfEmptyStructToFn() {
+    @setFnTest(this);
+
+    assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{ EmptyStruct2{} }) == 1);
+}
+fn testPassSliceOfEmptyStructToFn(slice: []EmptyStruct2) -> usize {
+    slice.len
+}
 
 
 // TODO const assert = @import("std").debug.assert;
test/self_hosted.zig
@@ -25,16 +25,3 @@ const test_pointer_to_void_return_type_x = void{};
 fn testPointerToVoidReturnType2() -> &const void {
     return &test_pointer_to_void_return_type_x;
 }
-
-
-// TODO not passing (goes in struct.zig)
-fn passSliceOfEmptyStructToFn() {
-    @setFnTest(this);
-
-    assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{ EmptyStruct2{} }) == 1);
-}
-fn testPassSliceOfEmptyStructToFn(slice: []EmptyStruct2) -> usize {
-    slice.len
-}
-
-