Commit 3f776af3fa

Andrew Kelley <superjoe30@gmail.com>
2018-09-15 00:56:30
fix alignment of structs
closes #1248 closes #1052 closes #1154
1 parent 639c381
src/all_types.hpp
@@ -1012,13 +1012,13 @@ enum PtrLen {
 
 struct ZigTypePointer {
     ZigType *child_type;
+    ZigType *slice_parent;
     PtrLen ptr_len;
-    bool is_const;
-    bool is_volatile;
-    uint32_t alignment;
+    uint32_t explicit_alignment; // 0 means use ABI alignment
     uint32_t bit_offset;
     uint32_t unaligned_bit_count;
-    ZigType *slice_parent;
+    bool is_const;
+    bool is_volatile;
 };
 
 struct ZigTypeInt {
@@ -1046,32 +1046,35 @@ struct TypeStructField {
     size_t unaligned_bit_count;
     AstNode *decl_node;
 };
+
+enum ResolveStatus {
+    ResolveStatusUnstarted,
+    ResolveStatusInvalid,
+    ResolveStatusZeroBitsKnown,
+    ResolveStatusAlignmentKnown,
+    ResolveStatusSizeKnown,
+};
+
 struct ZigTypeStruct {
     AstNode *decl_node;
-    ContainerLayout layout;
-    uint32_t src_field_count;
-    uint32_t gen_field_count;
     TypeStructField *fields;
-    uint64_t size_bytes;
-    bool is_invalid; // true if any fields are invalid
-    bool is_slice;
     ScopeDecls *decls_scope;
+    uint64_t size_bytes;
+    HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name;
 
-    // set this flag temporarily to detect infinite loops
-    bool embedded_in_current;
-    bool reported_infinite_err;
-    // whether we've finished resolving it
-    bool complete;
+    uint32_t src_field_count;
+    uint32_t gen_field_count;
+
+    uint32_t abi_alignment; // known after ResolveStatusAlignmentKnown
+    ContainerLayout layout;
+    ResolveStatus resolve_status;
 
+    bool is_slice;
+    bool resolve_loop_flag; // set this flag temporarily to detect infinite loops
+    bool reported_infinite_err;
     // whether any of the fields require comptime
-    // the value is not valid until zero_bits_known == true
+    // known after ResolveStatusZeroBitsKnown
     bool requires_comptime;
-
-    bool zero_bits_loop_flag;
-    bool zero_bits_known;
-    uint32_t abi_alignment; // also figured out with zero_bits pass
-
-    HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name;
 };
 
 struct ZigTypeOptional {
src/analyze.cpp
@@ -23,6 +23,7 @@ static Error resolve_enum_type(CodeGen *g, ZigType *enum_type);
 static Error resolve_struct_type(CodeGen *g, ZigType *struct_type);
 
 static Error ATTRIBUTE_MUST_USE resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type);
+static Error ATTRIBUTE_MUST_USE resolve_struct_alignment(CodeGen *g, ZigType *struct_type);
 static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type);
 static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);
 static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
@@ -254,18 +255,42 @@ AstNode *type_decl_node(ZigType *type_entry) {
     zig_unreachable();
 }
 
-bool type_is_complete(ZigType *type_entry) {
+bool type_is_resolved(ZigType *type_entry, ResolveStatus status) {
     switch (type_entry->id) {
         case ZigTypeIdInvalid:
             zig_unreachable();
         case ZigTypeIdStruct:
-            return type_entry->data.structure.complete;
+            return type_entry->data.structure.resolve_status >= status;
         case ZigTypeIdEnum:
-            return type_entry->data.enumeration.complete;
+            switch (status) {
+                case ResolveStatusUnstarted:
+                    return true;
+                case ResolveStatusInvalid:
+                    zig_unreachable();
+                case ResolveStatusZeroBitsKnown:
+                    return type_entry->data.enumeration.zero_bits_known;
+                case ResolveStatusAlignmentKnown:
+                    return type_entry->data.enumeration.zero_bits_known;
+                case ResolveStatusSizeKnown:
+                    return type_entry->data.enumeration.complete;
+            }
+            zig_unreachable();
         case ZigTypeIdUnion:
-            return type_entry->data.unionation.complete;
+            switch (status) {
+                case ResolveStatusUnstarted:
+                    return true;
+                case ResolveStatusInvalid:
+                    zig_unreachable();
+                case ResolveStatusZeroBitsKnown:
+                    return type_entry->data.unionation.zero_bits_known;
+                case ResolveStatusAlignmentKnown:
+                    return type_entry->data.unionation.zero_bits_known;
+                case ResolveStatusSizeKnown:
+                    return type_entry->data.unionation.complete;
+            }
+            zig_unreachable();
         case ZigTypeIdOpaque:
-            return false;
+            return status < ResolveStatusSizeKnown;
         case ZigTypeIdMetaType:
         case ZigTypeIdVoid:
         case ZigTypeIdBool:
@@ -291,43 +316,10 @@ bool type_is_complete(ZigType *type_entry) {
     zig_unreachable();
 }
 
-bool type_has_zero_bits_known(ZigType *type_entry) {
-    switch (type_entry->id) {
-        case ZigTypeIdInvalid:
-            zig_unreachable();
-        case ZigTypeIdStruct:
-            return type_entry->data.structure.zero_bits_known;
-        case ZigTypeIdEnum:
-            return type_entry->data.enumeration.zero_bits_known;
-        case ZigTypeIdUnion:
-            return type_entry->data.unionation.zero_bits_known;
-        case ZigTypeIdMetaType:
-        case ZigTypeIdVoid:
-        case ZigTypeIdBool:
-        case ZigTypeIdUnreachable:
-        case ZigTypeIdInt:
-        case ZigTypeIdFloat:
-        case ZigTypeIdPointer:
-        case ZigTypeIdArray:
-        case ZigTypeIdComptimeFloat:
-        case ZigTypeIdComptimeInt:
-        case ZigTypeIdUndefined:
-        case ZigTypeIdNull:
-        case ZigTypeIdOptional:
-        case ZigTypeIdErrorUnion:
-        case ZigTypeIdErrorSet:
-        case ZigTypeIdFn:
-        case ZigTypeIdNamespace:
-        case ZigTypeIdBoundFn:
-        case ZigTypeIdArgTuple:
-        case ZigTypeIdOpaque:
-        case ZigTypeIdPromise:
-            return true;
-    }
-    zig_unreachable();
+bool type_is_complete(ZigType *type_entry) {
+    return type_is_resolved(type_entry, ResolveStatusSizeKnown);
 }
 
-
 uint64_t type_size(CodeGen *g, ZigType *type_entry) {
     assert(type_is_complete(type_entry));
 
@@ -376,7 +368,7 @@ uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) {
 
 Result<bool> type_is_copyable(CodeGen *g, ZigType *type_entry) {
     Error err;
-    if ((err = type_ensure_zero_bits_known(g, type_entry)))
+    if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
         return err;
 
     if (!type_has_bits(type_entry))
@@ -431,10 +423,15 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
     assert(!type_is_invalid(child_type));
     assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque);
 
+    if (byte_alignment != 0) {
+        uint32_t abi_alignment = get_abi_alignment(g, child_type);
+        if (byte_alignment == abi_alignment)
+            byte_alignment = 0;
+    }
+
     TypeId type_id = {};
     ZigType **parent_pointer = nullptr;
-    uint32_t abi_alignment = get_abi_alignment(g, child_type);
-    if (unaligned_bit_count != 0 || is_volatile || byte_alignment != abi_alignment || ptr_len != PtrLenSingle) {
+    if (unaligned_bit_count != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle) {
         type_id.id = ZigTypeIdPointer;
         type_id.data.pointer.child_type = child_type;
         type_id.data.pointer.is_const = is_const;
@@ -451,12 +448,12 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
         assert(bit_offset == 0);
         parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)];
         if (*parent_pointer) {
-            assert((*parent_pointer)->data.pointer.alignment == byte_alignment);
+            assert((*parent_pointer)->data.pointer.explicit_alignment == 0);
             return *parent_pointer;
         }
     }
 
-    assertNoError(type_ensure_zero_bits_known(g, child_type));
+    assert(type_is_resolved(child_type, ResolveStatusZeroBitsKnown));
 
     ZigType *entry = new_type_table_entry(ZigTypeIdPointer);
     entry->is_copyable = true;
@@ -465,11 +462,14 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
     const char *const_str = is_const ? "const " : "";
     const char *volatile_str = is_volatile ? "volatile " : "";
     buf_resize(&entry->name, 0);
-    if (unaligned_bit_count == 0 && byte_alignment == abi_alignment) {
+    if (unaligned_bit_count == 0 && byte_alignment == 0) {
         buf_appendf(&entry->name, "%s%s%s%s", star_str, const_str, volatile_str, buf_ptr(&child_type->name));
     } else if (unaligned_bit_count == 0) {
         buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s", star_str, byte_alignment,
                 const_str, volatile_str, buf_ptr(&child_type->name));
+    } else if (byte_alignment == 0) {
+        buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str,
+                bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name));
     } else {
         buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str, byte_alignment,
                 bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name));
@@ -480,8 +480,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
     entry->zero_bits = !type_has_bits(child_type);
 
     if (!entry->zero_bits) {
-        assert(byte_alignment > 0);
-        if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != abi_alignment ||
+        if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != 0 ||
             ptr_len != PtrLenSingle)
         {
             ZigType *peer_type = get_pointer_to_type(g, child_type, false);
@@ -505,7 +504,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
     entry->data.pointer.child_type = child_type;
     entry->data.pointer.is_const = is_const;
     entry->data.pointer.is_volatile = is_volatile;
-    entry->data.pointer.alignment = byte_alignment;
+    entry->data.pointer.explicit_alignment = byte_alignment;
     entry->data.pointer.bit_offset = bit_offset;
     entry->data.pointer.unaligned_bit_count = unaligned_bit_count;
 
@@ -518,8 +517,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
 }
 
 ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) {
-    return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle,
-            get_abi_alignment(g, child_type), 0, 0);
+    return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0);
 }
 
 ZigType *get_promise_frame_type(CodeGen *g, ZigType *return_type) {
@@ -800,8 +798,7 @@ static void slice_type_common_init(CodeGen *g, ZigType *pointer_type, ZigType *e
     entry->data.structure.fields_by_name.put(ptr_field_name, &entry->data.structure.fields[slice_ptr_index]);
     entry->data.structure.fields_by_name.put(len_field_name, &entry->data.structure.fields[slice_len_index]);
 
-    assert(type_has_zero_bits_known(pointer_type->data.pointer.child_type));
-    if (pointer_type->data.pointer.child_type->zero_bits) {
+    if (!type_has_bits(pointer_type->data.pointer.child_type)) {
         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;
@@ -826,20 +823,18 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
     buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + name_offset);
 
     ZigType *child_type = ptr_type->data.pointer.child_type;
-    uint32_t abi_alignment = get_abi_alignment(g, child_type);
     if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile ||
-        ptr_type->data.pointer.alignment != abi_alignment)
+        ptr_type->data.pointer.explicit_alignment != 0)
     {
         ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false,
-                PtrLenUnknown, abi_alignment, 0, 0);
+                PtrLenUnknown, 0, 0, 0);
         ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);
 
         slice_type_common_init(g, ptr_type, entry);
 
         entry->type_ref = peer_slice_type->type_ref;
         entry->di_type = peer_slice_type->di_type;
-        entry->data.structure.complete = true;
-        entry->data.structure.zero_bits_known = true;
+        entry->data.structure.resolve_status = ResolveStatusSizeKnown;
         entry->data.structure.abi_alignment = peer_slice_type->data.structure.abi_alignment;
 
         *parent_pointer = entry;
@@ -851,15 +846,15 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
     if (is_slice(child_type)) {
         ZigType *child_ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry;
         assert(child_ptr_type->id == ZigTypeIdPointer);
-        ZigType *grand_child_type = child_ptr_type->data.pointer.child_type;
         if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile ||
-            child_ptr_type->data.pointer.alignment != get_abi_alignment(g, grand_child_type))
+            child_ptr_type->data.pointer.explicit_alignment != 0)
         {
+            ZigType *grand_child_type = child_ptr_type->data.pointer.child_type;
             ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false,
-                    PtrLenUnknown, get_abi_alignment(g, grand_child_type), 0, 0);
+                    PtrLenUnknown, 0, 0, 0);
             ZigType *bland_child_slice = get_slice_type(g, bland_child_ptr_type);
             ZigType *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false,
-                    PtrLenUnknown, get_abi_alignment(g, bland_child_slice), 0, 0);
+                    PtrLenUnknown, 0, 0, 0);
             ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);
 
             entry->type_ref = peer_slice_type->type_ref;
@@ -961,8 +956,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
     }
 
 
-    entry->data.structure.complete = true;
-    entry->data.structure.zero_bits_known = true;
+    entry->data.structure.resolve_status = ResolveStatusSizeKnown;
 
     *parent_pointer = entry;
     return entry;
@@ -1367,7 +1361,7 @@ static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_
 
 static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) {
     ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
-            PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
+            PtrLenUnknown, 0, 0, 0);
     ZigType *str_type = get_slice_type(g, ptr_type);
     IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr);
     if (type_is_invalid(instr->value.type))
@@ -1576,7 +1570,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
             return g->builtin_types.entry_invalid;
         }
         if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
-            if ((err = type_ensure_zero_bits_known(g, type_entry)))
+            if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
                 return g->builtin_types.entry_invalid;
             if (!type_has_bits(type_entry)) {
                 add_node_error(g, param_node->data.param_decl.type,
@@ -1624,7 +1618,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
             case ZigTypeIdUnion:
             case ZigTypeIdFn:
             case ZigTypeIdPromise:
-                if ((err = type_ensure_zero_bits_known(g, type_entry)))
+                if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
                     return g->builtin_types.entry_invalid;
                 if (type_requires_comptime(type_entry)) {
                     add_node_error(g, param_node->data.param_decl.type,
@@ -1714,7 +1708,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
         case ZigTypeIdUnion:
         case ZigTypeIdFn:
         case ZigTypeIdPromise:
-            if ((err = type_ensure_zero_bits_known(g, fn_type_id.return_type)))
+            if ((err = type_resolve(g, fn_type_id.return_type, ResolveStatusZeroBitsKnown)))
                 return g->builtin_types.entry_invalid;
             if (type_requires_comptime(fn_type_id.return_type)) {
                 return get_generic_fn_type(g, &fn_type_id);
@@ -1740,7 +1734,7 @@ bool type_is_invalid(ZigType *type_entry) {
         case ZigTypeIdInvalid:
             return true;
         case ZigTypeIdStruct:
-            return type_entry->data.structure.is_invalid;
+            return type_entry->data.structure.resolve_status == ResolveStatusInvalid;
         case ZigTypeIdEnum:
             return type_entry->data.enumeration.is_invalid;
         case ZigTypeIdUnion:
@@ -1855,8 +1849,7 @@ ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_na
 
     struct_type->data.structure.src_field_count = field_count;
     struct_type->data.structure.gen_field_count = 0;
-    struct_type->data.structure.zero_bits_known = true;
-    struct_type->data.structure.complete = true;
+    struct_type->data.structure.resolve_status = ResolveStatusSizeKnown;
     struct_type->data.structure.fields = allocate<TypeStructField>(field_count);
     struct_type->data.structure.fields_by_name.init(field_count);
 
@@ -1928,26 +1921,29 @@ ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_na
 static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
     assert(struct_type->id == ZigTypeIdStruct);
 
-    if (struct_type->data.structure.complete)
+    Error err;
+
+    if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
+        return ErrorSemanticAnalyzeFail;
+    if (struct_type->data.structure.resolve_status >= ResolveStatusSizeKnown)
         return ErrorNone;
 
-    Error err;
-    if ((err = resolve_struct_zero_bits(g, struct_type)))
+    if ((err = resolve_struct_alignment(g, struct_type)))
         return err;
 
     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;
+    if (struct_type->data.structure.resolve_loop_flag) {
+        if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
+            struct_type->data.structure.resolve_status = ResolveStatusInvalid;
             add_node_error(g, decl_node,
-                    buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name)));
+                buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name)));
         }
         return ErrorSemanticAnalyzeFail;
     }
 
-    assert(!struct_type->data.structure.zero_bits_loop_flag);
+    struct_type->data.structure.resolve_loop_flag = true;
+
     assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0);
     assert(decl_node->type == NodeTypeContainerDecl);
 
@@ -1956,9 +1952,6 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
     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;
 
     size_t gen_field_index = 0;
@@ -1972,7 +1965,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
         ZigType *field_type = type_struct_field->type_entry;
 
         if ((err = ensure_complete_type(g, field_type))) {
-            struct_type->data.structure.is_invalid = true;
+            struct_type->data.structure.resolve_status = ResolveStatusInvalid;
             break;
         }
 
@@ -1982,7 +1975,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
                 add_node_error(g, field_source_node,
                         buf_sprintf("extern structs cannot contain fields of type '%s'",
                             buf_ptr(&field_type->name)));
-                struct_type->data.structure.is_invalid = true;
+                struct_type->data.structure.resolve_status = ResolveStatusInvalid;
                 break;
             }
         }
@@ -1998,7 +1991,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
                 add_node_error(g, field_source_node,
                         buf_sprintf("packed structs cannot contain fields of type '%s'",
                             buf_ptr(&field_type->name)));
-                struct_type->data.structure.is_invalid = true;
+                struct_type->data.structure.resolve_status = ResolveStatusInvalid;
                 break;
             }
 
@@ -2049,12 +2042,13 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
         gen_field_index += 1;
     }
 
-    struct_type->data.structure.embedded_in_current = false;
-    struct_type->data.structure.complete = true;
+    struct_type->data.structure.resolve_loop_flag = false;
 
-    if (struct_type->data.structure.is_invalid)
+    if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
         return ErrorSemanticAnalyzeFail;
 
+    struct_type->data.structure.resolve_status = ResolveStatusSizeKnown;
+
     if (struct_type->zero_bits) {
         struct_type->type_ref = LLVMVoidType();
 
@@ -2116,7 +2110,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
 
         assert(field_type->type_ref);
         assert(struct_type->type_ref);
-        assert(struct_type->data.structure.complete);
+        assert(struct_type->data.structure.resolve_status == ResolveStatusSizeKnown);
         uint64_t debug_size_in_bits;
         uint64_t debug_align_in_bits;
         uint64_t debug_offset_in_bits;
@@ -2570,30 +2564,18 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
 
     Error err;
 
-    if (struct_type->data.structure.is_invalid)
+    if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
         return ErrorSemanticAnalyzeFail;
-
-    if (struct_type->data.structure.zero_bits_known)
+    if (struct_type->data.structure.resolve_status >= ResolveStatusZeroBitsKnown)
         return ErrorNone;
 
-    if (struct_type->data.structure.zero_bits_loop_flag) {
-        // If we get here it's due to recursion. This is a design flaw in the compiler,
-        // we should be able to still figure out alignment, but here we give up and say that
-        // the alignment is pointer width, then assert that the first field is within that
-        // alignment
-        struct_type->data.structure.zero_bits_known = true;
-        struct_type->data.structure.zero_bits_loop_flag = false;
-        if (struct_type->data.structure.abi_alignment == 0) {
-            if (struct_type->data.structure.layout == ContainerLayoutPacked) {
-                struct_type->data.structure.abi_alignment = 1;
-            } else {
-                struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0));
-            }
-        }
+    if (struct_type->data.structure.resolve_loop_flag) {
+        struct_type->data.structure.resolve_status = ResolveStatusZeroBitsKnown;
+        struct_type->data.structure.resolve_loop_flag = false;
         return ErrorNone;
     }
 
-    struct_type->data.structure.zero_bits_loop_flag = true;
+    struct_type->data.structure.resolve_loop_flag = true;
 
     AstNode *decl_node = struct_type->data.structure.decl_node;
     assert(decl_node->type == NodeTypeContainerDecl);
@@ -2616,7 +2598,7 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
 
         if (field_node->data.struct_field.type == nullptr) {
             add_node_error(g, field_node, buf_sprintf("struct field missing type"));
-            struct_type->data.structure.is_invalid = true;
+            struct_type->data.structure.resolve_status = ResolveStatusInvalid;
             continue;
         }
 
@@ -2625,7 +2607,7 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
             ErrorMsg *msg = add_node_error(g, field_node,
                 buf_sprintf("duplicate struct field: '%s'", buf_ptr(type_struct_field->name)));
             add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
-            struct_type->data.structure.is_invalid = true;
+            struct_type->data.structure.resolve_status = ResolveStatusInvalid;
             continue;
         }
 
@@ -2639,8 +2621,8 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
                     buf_sprintf("enums, not structs, support field assignment"));
         }
 
-        if ((err = type_ensure_zero_bits_known(g, field_type))) {
-            struct_type->data.structure.is_invalid = true;
+        if ((err = type_resolve(g, field_type, ResolveStatusZeroBitsKnown))) {
+            struct_type->data.structure.resolve_status = ResolveStatusInvalid;
             continue;
         }
 
@@ -2651,36 +2633,87 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
         if (!type_has_bits(field_type))
             continue;
 
-        if (gen_field_index == 0) {
-            if (struct_type->data.structure.layout == ContainerLayoutPacked) {
-                struct_type->data.structure.abi_alignment = 1;
-            } else if (struct_type->data.structure.abi_alignment == 0) {
-                // Alignment of structs is the alignment of the first field, for now.
-                // TODO change this when we re-order struct fields (issue #168)
-                struct_type->data.structure.abi_alignment = get_abi_alignment(g, field_type);
-                assert(struct_type->data.structure.abi_alignment != 0);
-            } else {
-                // due to a design flaw in the compiler we assumed that alignment was
-                // pointer width, so we assert that this wasn't violated.
-                if (get_abi_alignment(g, field_type) > struct_type->data.structure.abi_alignment) {
-                    zig_panic("compiler design flaw: incorrect alignment assumption");
-                }
-            }
-        }
-
         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.resolve_loop_flag = false;
     struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index;
     struct_type->zero_bits = (gen_field_index == 0);
-    struct_type->data.structure.zero_bits_known = true;
 
-    if (struct_type->data.structure.is_invalid) {
+    if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
+        return ErrorSemanticAnalyzeFail;
+
+    struct_type->data.structure.resolve_status = ResolveStatusZeroBitsKnown;
+    return ErrorNone;
+}
+
+static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) {
+    assert(struct_type->id == ZigTypeIdStruct);
+
+    Error err;
+
+    if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
+        return ErrorSemanticAnalyzeFail;
+    if (struct_type->data.structure.resolve_status >= ResolveStatusAlignmentKnown)
+        return ErrorNone;
+
+    if ((err = resolve_struct_zero_bits(g, struct_type)))
+        return err;
+
+    AstNode *decl_node = struct_type->data.structure.decl_node;
+
+    if (struct_type->data.structure.resolve_loop_flag) {
+        if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
+            struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+            add_node_error(g, decl_node,
+                buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name)));
+        }
         return ErrorSemanticAnalyzeFail;
     }
 
+    struct_type->data.structure.resolve_loop_flag = true;
+    assert(decl_node->type == NodeTypeContainerDecl);
+    assert(struct_type->di_type);
+
+    if (struct_type->data.structure.layout == ContainerLayoutPacked) {
+        struct_type->data.structure.abi_alignment = 1;
+    }
+
+    size_t field_count = struct_type->data.structure.src_field_count;
+    for (size_t i = 0; i < field_count; i += 1) {
+        TypeStructField *field = &struct_type->data.structure.fields[i];
+
+        // If this assertion trips, look up the call stack. Probably something is
+        // calling type_resolve with ResolveStatusAlignmentKnown when it should only
+        // be resolving ResolveStatusZeroBitsKnown
+        assert(field->type_entry != nullptr);
+
+        if (!type_has_bits(field->type_entry))
+            continue;
+
+        // alignment of structs is the alignment of the most-aligned field
+        if (struct_type->data.structure.layout != ContainerLayoutPacked) {
+            if ((err = type_resolve(g, field->type_entry, ResolveStatusAlignmentKnown))) {
+                struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+                break;
+            }
+
+            uint32_t this_field_align = get_abi_alignment(g, field->type_entry);
+            assert(this_field_align != 0);
+            if (this_field_align > struct_type->data.structure.abi_alignment) {
+                struct_type->data.structure.abi_alignment = this_field_align;
+            }
+        }
+    }
+
+    struct_type->data.structure.resolve_loop_flag = false;
+
+    if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) {
+        return ErrorSemanticAnalyzeFail;
+    }
+
+    struct_type->data.structure.resolve_status = ResolveStatusAlignmentKnown;
     return ErrorNone;
 }
 
@@ -2807,7 +2840,7 @@ 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))) {
+        if ((err = type_resolve(g, enum_type, ResolveStatusAlignmentKnown))) {
             assert(g->errors.length != 0);
             return err;
         }
@@ -2848,7 +2881,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
             }
         } else {
             field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
-            if ((err = type_ensure_zero_bits_known(g, field_type))) {
+            if ((err = type_resolve(g, field_type, ResolveStatusAlignmentKnown))) {
                 union_type->data.unionation.is_invalid = true;
                 continue;
             }
@@ -3111,7 +3144,7 @@ static void typecheck_panic_fn(CodeGen *g, ZigFn *panic_fn) {
         return wrong_panic_prototype(g, proto_node, fn_type);
     }
     ZigType *const_u8_ptr = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
-            PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
+            PtrLenUnknown, 0, 0, 0);
     ZigType *const_u8_slice = get_slice_type(g, const_u8_ptr);
     if (fn_type_id->param_info[0].type != const_u8_slice) {
         return wrong_panic_prototype(g, proto_node, fn_type);
@@ -3801,7 +3834,7 @@ TypeEnumField *find_enum_type_field(ZigType *enum_type, Buf *name) {
 
 TypeStructField *find_struct_type_field(ZigType *type_entry, Buf *name) {
     assert(type_entry->id == ZigTypeIdStruct);
-    assert(type_entry->data.structure.complete);
+    assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
     if (type_entry->data.structure.src_field_count == 0)
         return nullptr;
     auto entry = type_entry->data.structure.fields_by_name.maybe_get(name);
@@ -3956,9 +3989,12 @@ bool type_is_codegen_pointer(ZigType *type) {
 uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
     ZigType *ptr_type = get_codegen_ptr_type(type);
     if (ptr_type->id == ZigTypeIdPointer) {
-        return ptr_type->data.pointer.alignment;
+        return (ptr_type->data.pointer.explicit_alignment == 0) ?
+            get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;
     } else if (ptr_type->id == ZigTypeIdFn) {
-        return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment;
+        return (ptr_type->data.fn.fn_type_id.alignment == 0) ?
+            LLVMABIAlignmentOfType(g->target_data_ref, ptr_type->data.fn.raw_type_ref) :
+            ptr_type->data.fn.fn_type_id.alignment;
     } else if (ptr_type->id == ZigTypeIdPromise) {
         return get_coro_frame_align_bytes(g);
     } else {
@@ -5023,8 +5059,8 @@ bool fn_eval_eql(Scope *a, Scope *b) {
 
 bool type_has_bits(ZigType *type_entry) {
     assert(type_entry);
-    assert(type_entry->id != ZigTypeIdInvalid);
-    assert(type_has_zero_bits_known(type_entry));
+    assert(!type_is_invalid(type_entry));
+    assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
     return !type_entry->zero_bits;
 }
 
@@ -5045,10 +5081,10 @@ bool type_requires_comptime(ZigType *type_entry) {
         case ZigTypeIdArray:
             return type_requires_comptime(type_entry->data.array.child_type);
         case ZigTypeIdStruct:
-            assert(type_has_zero_bits_known(type_entry));
+            assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
             return type_entry->data.structure.requires_comptime;
         case ZigTypeIdUnion:
-            assert(type_has_zero_bits_known(type_entry));
+            assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
             return type_entry->data.unionation.requires_comptime;
         case ZigTypeIdOptional:
             return type_requires_comptime(type_entry->data.maybe.child_type);
@@ -5124,7 +5160,7 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
     const_val->special = ConstValSpecialStatic;
     // TODO make this `[*]null u8` instead of `[*]u8`
     const_val->type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
-            PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
+            PtrLenUnknown, 0, 0, 0);
     const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
     const_val->data.x_ptr.data.base_array.array_val = array_val;
     const_val->data.x_ptr.data.base_array.elem_index = 0;
@@ -5269,8 +5305,7 @@ void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *arr
     assert(array_val->type->id == ZigTypeIdArray);
 
     ZigType *ptr_type = get_pointer_to_type_extra(g, array_val->type->data.array.child_type,
-            is_const, false, PtrLenUnknown, get_abi_alignment(g, array_val->type->data.array.child_type),
-            0, 0);
+            is_const, false, PtrLenUnknown, 0, 0, 0);
 
     const_val->special = ConstValSpecialStatic;
     const_val->type = get_slice_type(g, ptr_type);
@@ -5295,7 +5330,7 @@ void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue
 
     const_val->special = ConstValSpecialStatic;
     const_val->type = get_pointer_to_type_extra(g, child_type, is_const, false,
-            ptr_len, get_abi_alignment(g, child_type), 0, 0);
+            ptr_len, 0, 0, 0);
     const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
     const_val->data.x_ptr.data.base_array.array_val = array_val;
     const_val->data.x_ptr.data.base_array.elem_index = elem_index;
@@ -5394,32 +5429,46 @@ ConstExprValue *create_const_vals(size_t count) {
 }
 
 Error ensure_complete_type(CodeGen *g, ZigType *type_entry) {
-    if (type_is_invalid(type_entry))
-        return ErrorSemanticAnalyzeFail;
-    if (type_entry->id == ZigTypeIdStruct) {
-        if (!type_entry->data.structure.complete)
-            return resolve_struct_type(g, type_entry);
-    } else if (type_entry->id == ZigTypeIdEnum) {
-        if (!type_entry->data.enumeration.complete)
-            return resolve_enum_type(g, type_entry);
-    } else if (type_entry->id == ZigTypeIdUnion) {
-        if (!type_entry->data.unionation.complete)
-            return resolve_union_type(g, type_entry);
-    }
-    return ErrorNone;
+    return type_resolve(g, type_entry, ResolveStatusSizeKnown);
 }
 
-Error type_ensure_zero_bits_known(CodeGen *g, ZigType *type_entry) {
-    if (type_is_invalid(type_entry))
+Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) {
+    if (type_is_invalid(ty))
         return ErrorSemanticAnalyzeFail;
-    if (type_entry->id == ZigTypeIdStruct) {
-        return resolve_struct_zero_bits(g, type_entry);
-    } else if (type_entry->id == ZigTypeIdEnum) {
-        return resolve_enum_zero_bits(g, type_entry);
-    } else if (type_entry->id == ZigTypeIdUnion) {
-        return resolve_union_zero_bits(g, type_entry);
+    switch (status) {
+        case ResolveStatusUnstarted:
+            return ErrorNone;
+        case ResolveStatusInvalid:
+            zig_unreachable();
+        case ResolveStatusZeroBitsKnown:
+            if (ty->id == ZigTypeIdStruct) {
+                return resolve_struct_zero_bits(g, ty);
+            } else if (ty->id == ZigTypeIdEnum) {
+                return resolve_enum_zero_bits(g, ty);
+            } else if (ty->id == ZigTypeIdUnion) {
+                return resolve_union_zero_bits(g, ty);
+            }
+            return ErrorNone;
+        case ResolveStatusAlignmentKnown:
+            if (ty->id == ZigTypeIdStruct) {
+                return resolve_struct_alignment(g, ty);
+            } else if (ty->id == ZigTypeIdEnum) {
+                return resolve_enum_zero_bits(g, ty);
+            } else if (ty->id == ZigTypeIdUnion) {
+                return resolve_union_zero_bits(g, ty);
+            }
+            return ErrorNone;
+        case ResolveStatusSizeKnown:
+            if (ty->id == ZigTypeIdStruct) {
+                return resolve_struct_type(g, ty);
+            } else if (ty->id == ZigTypeIdEnum) {
+                return resolve_enum_type(g, ty);
+            } else if (ty->id == ZigTypeIdUnion) {
+                return resolve_union_type(g, ty);
+            }
+            return ErrorNone;
     }
-    return ErrorNone;
+    zig_unreachable();
 }
 
 bool ir_get_var_is_comptime(ZigVar *var) {
@@ -6262,7 +6311,7 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) {
 }
 
 uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry) {
-    assertNoError(type_ensure_zero_bits_known(g, type_entry));
+    assert(type_is_resolved(type_entry, ResolveStatusAlignmentKnown));
     if (type_entry->zero_bits) return 0;
 
     // We need to make this function work without requiring ensure_complete_type
src/analyze.hpp
@@ -59,9 +59,9 @@ bool get_ptr_const(ZigType *type);
 ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry);
 ZigType *container_ref_type(ZigType *type_entry);
 bool type_is_complete(ZigType *type_entry);
+bool type_is_resolved(ZigType *type_entry, ResolveStatus status);
 bool type_is_invalid(ZigType *type_entry);
 bool type_is_global_error_set(ZigType *err_set_type);
-bool type_has_zero_bits_known(ZigType *type_entry);
 void resolve_container_type(CodeGen *g, ZigType *type_entry);
 ScopeDecls *get_container_scope(ZigType *type_entry);
 TypeStructField *find_struct_type_field(ZigType *type_entry, Buf *name);
@@ -89,7 +89,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
 AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index);
 bool type_requires_comptime(ZigType *type_entry);
 Error ATTRIBUTE_MUST_USE ensure_complete_type(CodeGen *g, ZigType *type_entry);
-Error ATTRIBUTE_MUST_USE type_ensure_zero_bits_known(CodeGen *g, ZigType *type_entry);
+Error ATTRIBUTE_MUST_USE type_resolve(CodeGen *g, ZigType *type_entry, ResolveStatus status);
 void complete_enum(CodeGen *g, ZigType *enum_type);
 bool ir_get_var_is_comptime(ZigVar *var);
 bool const_values_equal(ConstExprValue *a, ConstExprValue *b);
src/codegen.cpp
@@ -775,7 +775,8 @@ static LLVMValueRef gen_store_untyped(CodeGen *g, LLVMValueRef value, LLVMValueR
 
 static LLVMValueRef gen_store(CodeGen *g, LLVMValueRef value, LLVMValueRef ptr, ZigType *ptr_type) {
     assert(ptr_type->id == ZigTypeIdPointer);
-    return gen_store_untyped(g, value, ptr, ptr_type->data.pointer.alignment, ptr_type->data.pointer.is_volatile);
+    uint32_t alignment = get_ptr_align(g, ptr_type);
+    return gen_store_untyped(g, value, ptr, alignment, ptr_type->data.pointer.is_volatile);
 }
 
 static LLVMValueRef gen_load_untyped(CodeGen *g, LLVMValueRef ptr, uint32_t alignment, bool is_volatile,
@@ -793,7 +794,8 @@ static LLVMValueRef gen_load_untyped(CodeGen *g, LLVMValueRef ptr, uint32_t alig
 
 static LLVMValueRef gen_load(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, const char *name) {
     assert(ptr_type->id == ZigTypeIdPointer);
-    return gen_load_untyped(g, ptr, ptr_type->data.pointer.alignment, ptr_type->data.pointer.is_volatile, name);
+    uint32_t alignment = get_ptr_align(g, ptr_type);
+    return gen_load_untyped(g, ptr, alignment, ptr_type->data.pointer.is_volatile, name);
 }
 
 static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, ZigType *type, ZigType *ptr_type) {
@@ -1795,7 +1797,7 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_ty
 
         ZigType *usize = g->builtin_types.entry_usize;
         uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, child_type->type_ref);
-        uint64_t align_bytes = ptr_type->data.pointer.alignment;
+        uint64_t align_bytes = get_ptr_align(g, ptr_type);
         assert(size_bytes > 0);
         assert(align_bytes > 0);
 
@@ -4084,7 +4086,7 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
     LLVMValueRef ptr_val;
 
     if (target_type->id == ZigTypeIdPointer) {
-        align_bytes = target_type->data.pointer.alignment;
+        align_bytes = get_ptr_align(g, target_type);
         ptr_val = target_val;
     } else if (target_type->id == ZigTypeIdFn) {
         align_bytes = target_type->data.fn.fn_type_id.alignment;
@@ -4092,7 +4094,7 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
     } else if (target_type->id == ZigTypeIdOptional &&
             target_type->data.maybe.child_type->id == ZigTypeIdPointer)
     {
-        align_bytes = target_type->data.maybe.child_type->data.pointer.alignment;
+        align_bytes = get_ptr_align(g, target_type->data.maybe.child_type);
         ptr_val = target_val;
     } else if (target_type->id == ZigTypeIdOptional &&
             target_type->data.maybe.child_type->id == ZigTypeIdFn)
@@ -4105,7 +4107,7 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
         zig_panic("TODO audit this function");
     } else if (target_type->id == ZigTypeIdStruct && target_type->data.structure.is_slice) {
         ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry;
-        align_bytes = slice_ptr_type->data.pointer.alignment;
+        align_bytes = get_ptr_align(g, slice_ptr_type);
 
         size_t ptr_index = target_type->data.structure.fields[slice_ptr_index].gen_index;
         LLVMValueRef ptr_val_ptr = LLVMBuildStructGEP(g->builder, target_val, (unsigned)ptr_index, "");
@@ -4260,7 +4262,8 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrIns
     LLVMValueRef is_volatile = ptr_type->data.pointer.is_volatile ?
         LLVMConstAllOnes(LLVMInt1Type()) : LLVMConstNull(LLVMInt1Type());
 
-    LLVMValueRef align_val = LLVMConstInt(LLVMInt32Type(), ptr_type->data.pointer.alignment, false);
+    uint32_t alignment = get_ptr_align(g, ptr_type);
+    LLVMValueRef align_val = LLVMConstInt(LLVMInt32Type(), alignment, false);
 
     LLVMValueRef params[] = {
         dest_ptr_casted,
@@ -4293,7 +4296,7 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutable *executable, IrIns
     LLVMValueRef is_volatile = (dest_ptr_type->data.pointer.is_volatile || src_ptr_type->data.pointer.is_volatile) ?
         LLVMConstAllOnes(LLVMInt1Type()) : LLVMConstNull(LLVMInt1Type());
 
-    uint32_t min_align_bytes = min(src_ptr_type->data.pointer.alignment, dest_ptr_type->data.pointer.alignment);
+    uint32_t min_align_bytes = min(get_ptr_align(g, src_ptr_type), get_ptr_align(g, dest_ptr_type));
     LLVMValueRef align_val = LLVMConstInt(LLVMInt32Type(), min_align_bytes, false);
 
     LLVMValueRef params[] = {
src/ir.cpp
@@ -40,6 +40,7 @@ struct IrAnalyze {
 
 enum ConstCastResultId {
     ConstCastResultIdOk,
+    ConstCastResultIdInvalid,
     ConstCastResultIdErrSet,
     ConstCastResultIdErrSetGlobal,
     ConstCastResultIdPointerChild,
@@ -7490,8 +7491,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
         if (type_has_bits(return_type)) {
             IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node,
                     get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
-                        false, false, PtrLenUnknown, get_abi_alignment(irb->codegen, irb->codegen->builtin_types.entry_u8),
-                        0, 0));
+                        false, false, PtrLenUnknown, 0, 0, 0));
             IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr);
             IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr);
             IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len,
@@ -7544,8 +7544,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
         IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle);
         IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node,
                 get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
-                    false, false, PtrLenUnknown, get_abi_alignment(irb->codegen, irb->codegen->builtin_types.entry_u8),
-                    0, 0));
+                    false, false, PtrLenUnknown, 0, 0, 0));
         IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe);
         IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false);
         IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var);
@@ -8516,6 +8515,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
     ConstCastOnly result = {};
     result.id = ConstCastResultIdOk;
 
+    Error err;
+
     if (wanted_type == actual_type)
         return result;
 
@@ -8528,6 +8529,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
     {
         ConstCastOnly child = types_match_const_cast_only(ira,
                 wanted_type->data.maybe.child_type, actual_type, source_node, wanted_is_mutable);
+        if (child.id == ConstCastResultIdInvalid)
+            return child;
         if (child.id != ConstCastResultIdOk) {
             result.id = ConstCastResultIdNullWrapPtr;
             result.data.null_wrap_ptr_child = allocate_nonzero<ConstCastOnly>(1);
@@ -8544,7 +8547,6 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
         (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) &&
         (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile))
     {
-        assert(actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment);
         return result;
     }
 
@@ -8552,6 +8554,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
     if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer) {
         ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
                 actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const);
+        if (child.id == ConstCastResultIdInvalid)
+            return child;
         if (child.id != ConstCastResultIdOk) {
             result.id = ConstCastResultIdPointerChild;
             result.data.pointer_mismatch = allocate_nonzero<ConstCastPointerMismatch>(1);
@@ -8560,12 +8564,20 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
             result.data.pointer_mismatch->actual_child = actual_type->data.pointer.child_type;
             return result;
         }
+        if ((err = type_resolve(g, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
+            result.id = ConstCastResultIdInvalid;
+            return result;
+        }
+        if ((err = type_resolve(g, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
+            result.id = ConstCastResultIdInvalid;
+            return result;
+        }
         if ((actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) &&
             (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) &&
             (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) &&
             actual_type->data.pointer.bit_offset == wanted_type->data.pointer.bit_offset &&
             actual_type->data.pointer.unaligned_bit_count == wanted_type->data.pointer.unaligned_bit_count &&
-            actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment)
+            get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type))
         {
             return result;
         }
@@ -8575,14 +8587,24 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
     if (is_slice(wanted_type) && is_slice(actual_type)) {
         ZigType *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry;
         ZigType *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
+        if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
+            result.id = ConstCastResultIdInvalid;
+            return result;
+        }
+        if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
+            result.id = ConstCastResultIdInvalid;
+            return result;
+        }
         if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
             (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
             actual_ptr_type->data.pointer.bit_offset == wanted_ptr_type->data.pointer.bit_offset &&
             actual_ptr_type->data.pointer.unaligned_bit_count == wanted_ptr_type->data.pointer.unaligned_bit_count &&
-            actual_ptr_type->data.pointer.alignment >= wanted_ptr_type->data.pointer.alignment)
+            get_ptr_align(g, actual_ptr_type) >= get_ptr_align(g, wanted_ptr_type))
         {
             ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type,
                     actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const);
+            if (child.id == ConstCastResultIdInvalid)
+                return child;
             if (child.id != ConstCastResultIdOk) {
                 result.id = ConstCastResultIdSliceChild;
                 result.data.slice_mismatch = allocate_nonzero<ConstCastSliceMismatch>(1);
@@ -8598,6 +8620,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
     if (wanted_type->id == ZigTypeIdOptional && actual_type->id == ZigTypeIdOptional) {
         ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type,
                 actual_type->data.maybe.child_type, source_node, wanted_is_mutable);
+        if (child.id == ConstCastResultIdInvalid)
+            return child;
         if (child.id != ConstCastResultIdOk) {
             result.id = ConstCastResultIdOptionalChild;
             result.data.optional = allocate_nonzero<ConstCastOptionalMismatch>(1);
@@ -8612,6 +8636,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
     if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorUnion) {
         ConstCastOnly payload_child = types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type,
                 actual_type->data.error_union.payload_type, source_node, wanted_is_mutable);
+        if (payload_child.id == ConstCastResultIdInvalid)
+            return payload_child;
         if (payload_child.id != ConstCastResultIdOk) {
             result.id = ConstCastResultIdErrorUnionPayload;
             result.data.error_union_payload = allocate_nonzero<ConstCastErrUnionPayloadMismatch>(1);
@@ -8622,6 +8648,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
         }
         ConstCastOnly error_set_child = types_match_const_cast_only(ira, wanted_type->data.error_union.err_set_type,
                 actual_type->data.error_union.err_set_type, source_node, wanted_is_mutable);
+        if (error_set_child.id == ConstCastResultIdInvalid)
+            return error_set_child;
         if (error_set_child.id != ConstCastResultIdOk) {
             result.id = ConstCastResultIdErrorUnionErrorSet;
             result.data.error_union_error_set = allocate_nonzero<ConstCastErrUnionErrSetMismatch>(1);
@@ -8709,6 +8737,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
         {
             ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.fn.fn_type_id.return_type,
                     actual_type->data.fn.fn_type_id.return_type, source_node, false);
+            if (child.id == ConstCastResultIdInvalid)
+                return child;
             if (child.id != ConstCastResultIdOk) {
                 result.id = ConstCastResultIdFnReturnType;
                 result.data.return_type = allocate_nonzero<ConstCastOnly>(1);
@@ -8721,6 +8751,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
                     actual_type->data.fn.fn_type_id.async_allocator_type,
                     wanted_type->data.fn.fn_type_id.async_allocator_type,
                     source_node, false);
+            if (child.id == ConstCastResultIdInvalid)
+                return child;
             if (child.id != ConstCastResultIdOk) {
                 result.id = ConstCastResultIdAsyncAllocatorType;
                 result.data.async_allocator_type = allocate_nonzero<ConstCastOnly>(1);
@@ -8745,6 +8777,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
 
             ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type,
                     expected_param_info->type, source_node, false);
+            if (arg_child.id == ConstCastResultIdInvalid)
+                return arg_child;
             if (arg_child.id != ConstCastResultIdOk) {
                 result.id = ConstCastResultIdFnArg;
                 result.data.fn_arg.arg_index = i;
@@ -9238,7 +9272,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
         if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdUnion &&
             (cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
         {
-            if ((err = type_ensure_zero_bits_known(ira->codegen, cur_type)))
+            if ((err = type_resolve(ira->codegen, cur_type, ResolveStatusZeroBitsKnown)))
                 return ira->codegen->builtin_types.entry_invalid;
             if (cur_type->data.unionation.tag_type == prev_type) {
                 continue;
@@ -9248,7 +9282,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
         if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdUnion &&
             (prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
         {
-            if ((err = type_ensure_zero_bits_known(ira->codegen, prev_type)))
+            if ((err = type_resolve(ira->codegen, prev_type, ResolveStatusZeroBitsKnown)))
                 return ira->codegen->builtin_types.entry_invalid;
             if (prev_type->data.unionation.tag_type == cur_type) {
                 prev_inst = cur_inst;
@@ -9274,8 +9308,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
         ZigType *ptr_type = get_pointer_to_type_extra(
                 ira->codegen, prev_inst->value.type->data.array.child_type,
                 true, false, PtrLenUnknown,
-                get_abi_alignment(ira->codegen, prev_inst->value.type->data.array.child_type),
-                0, 0);
+                0, 0, 0);
         ZigType *slice_type = get_slice_type(ira->codegen, ptr_type);
         if (err_set_type != nullptr) {
             return get_error_union_type(ira->codegen, err_set_type, slice_type);
@@ -9472,7 +9505,16 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira,
         IrInstruction *value, ZigType *wanted_type)
 {
     assert(value->value.type->id == ZigTypeIdPointer);
-    wanted_type = adjust_ptr_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment);
+
+    Error err;
+
+    if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type,
+                    ResolveStatusAlignmentKnown)))
+    {
+        return ira->codegen->invalid_instruction;
+    }
+
+    wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type));
 
     if (instr_is_comptime(value)) {
         ConstExprValue *pointee = ir_const_ptr_pointee(ira, &value->value, source_instr->source_node);
@@ -9500,7 +9542,15 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira,
 static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr,
         IrInstruction *value, ZigType *wanted_type)
 {
-    wanted_type = adjust_slice_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment);
+    Error err;
+
+    if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type,
+                    ResolveStatusAlignmentKnown)))
+    {
+        return ira->codegen->invalid_instruction;
+    }
+
+    wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type));
 
     if (instr_is_comptime(value)) {
         ConstExprValue *pointee = ir_const_ptr_pointee(ira, &value->value, source_instr->source_node);
@@ -9687,8 +9737,7 @@ static ZigType *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction,
         ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile)
 {
     IrInstruction *const_instr = ir_get_const_ptr(ira, instruction, pointee,
-            pointee_type, ptr_mut, ptr_is_const, ptr_is_volatile,
-            get_abi_alignment(ira->codegen, pointee_type));
+            pointee_type, ptr_mut, ptr_is_const, ptr_is_volatile, 0);
     ir_link_new_instruction(const_instr, instruction);
     return const_instr->value.type;
 }
@@ -10005,20 +10054,24 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so
 static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value,
         bool is_const, bool is_volatile)
 {
+    Error err;
+
     if (type_is_invalid(value->value.type))
         return ira->codegen->invalid_instruction;
 
+    if ((err = type_resolve(ira->codegen, value->value.type, ResolveStatusZeroBitsKnown)))
+        return ira->codegen->invalid_instruction;
+
     if (instr_is_comptime(value)) {
         ConstExprValue *val = ir_resolve_const(ira, value, UndefOk);
         if (!val)
             return ira->codegen->invalid_instruction;
         return ir_get_const_ptr(ira, source_instruction, val, value->value.type,
-                ConstPtrMutComptimeConst, is_const, is_volatile,
-                get_abi_alignment(ira->codegen, value->value.type));
+                ConstPtrMutComptimeConst, is_const, is_volatile, 0);
     }
 
     ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type,
-            is_const, is_volatile, PtrLenSingle, get_abi_alignment(ira->codegen, value->value.type), 0, 0);
+            is_const, is_volatile, PtrLenSingle, 0, 0, 0);
     IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope,
             source_instruction->source_node, value, is_const, is_volatile);
     new_instruction->value.type = ptr_type;
@@ -10185,9 +10238,9 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so
             return ira->codegen->invalid_instruction;
         TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);
         assert(union_field != nullptr);
-        if ((err = type_ensure_zero_bits_known(ira->codegen, union_field->type_entry)))
+        if ((err = type_resolve(ira->codegen, union_field->type_entry, ResolveStatusZeroBitsKnown)))
             return ira->codegen->invalid_instruction;
-        if (!union_field->type_entry->zero_bits) {
+        if (type_has_bits(union_field->type_entry)) {
             AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(
                     union_field->enum_field->decl_index);
             ErrorMsg *msg = ir_add_error(ira, source_instr,
@@ -10490,7 +10543,10 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou
         ZigType *wanted_type)
 {
     assert(wanted_type->id == ZigTypeIdPointer);
-    wanted_type = adjust_ptr_align(ira->codegen, wanted_type, target->value.type->data.pointer.alignment);
+    Error err;
+    if ((err = type_resolve(ira->codegen, target->value.type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
+        return ira->codegen->invalid_instruction;
+    wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, target->value.type));
     ZigType *array_type = wanted_type->data.pointer.child_type;
     assert(array_type->id == ZigTypeIdArray);
     assert(array_type->data.array.len == 1);
@@ -10537,6 +10593,8 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
     switch (cast_result->id) {
         case ConstCastResultIdOk:
             zig_unreachable();
+        case ConstCastResultIdInvalid:
+            zig_unreachable();
         case ConstCastResultIdOptionalChild: {
             ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
                     buf_sprintf("optional type child '%s' cannot cast into optional type child '%s'",
@@ -10636,6 +10694,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     // perfect match or non-const to const
     ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type,
             source_node, false);
+    if (const_cast_result.id == ConstCastResultIdInvalid)
+        return ira->codegen->invalid_instruction;
     if (const_cast_result.id == ConstCastResultIdOk) {
         return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
     }
@@ -10751,13 +10811,19 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         wanted_type->data.pointer.ptr_len == PtrLenUnknown &&
         actual_type->id == ZigTypeIdPointer &&
         actual_type->data.pointer.ptr_len == PtrLenSingle &&
-        actual_type->data.pointer.child_type->id == ZigTypeIdArray &&
-        actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment &&
-        types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
-            actual_type->data.pointer.child_type->data.array.child_type, source_node,
-            !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
+        actual_type->data.pointer.child_type->id == ZigTypeIdArray)
     {
-        return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type);
+        if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
+            return ira->codegen->invalid_instruction;
+        if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
+            return ira->codegen->invalid_instruction;
+        if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type) &&
+            types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
+                actual_type->data.pointer.child_type->data.array.child_type, source_node,
+                !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
+        {
+            return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type);
+        }
     }
 
     // *[N]T to []T
@@ -10811,16 +10877,23 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
             wanted_child_type->data.pointer.ptr_len == PtrLenUnknown &&
             actual_type->id == ZigTypeIdPointer &&
             actual_type->data.pointer.ptr_len == PtrLenSingle &&
-            actual_type->data.pointer.child_type->id == ZigTypeIdArray &&
-            actual_type->data.pointer.alignment >= wanted_child_type->data.pointer.alignment &&
-            types_match_const_cast_only(ira, wanted_child_type->data.pointer.child_type,
-            actual_type->data.pointer.child_type->data.array.child_type, source_node,
-            !wanted_child_type->data.pointer.is_const).id == ConstCastResultIdOk)
+            actual_type->data.pointer.child_type->id == ZigTypeIdArray)
         {
-            IrInstruction *cast1 = ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_child_type);
-            if (type_is_invalid(cast1->value.type))
+            if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
                 return ira->codegen->invalid_instruction;
-            return ir_analyze_maybe_wrap(ira, source_instr, cast1, wanted_type);
+            if ((err = type_resolve(ira->codegen, wanted_child_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
+                return ira->codegen->invalid_instruction;
+            if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_child_type) &&
+                types_match_const_cast_only(ira, wanted_child_type->data.pointer.child_type,
+                actual_type->data.pointer.child_type->data.array.child_type, source_node,
+                !wanted_child_type->data.pointer.is_const).id == ConstCastResultIdOk)
+            {
+                IrInstruction *cast1 = ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value,
+                        wanted_child_type);
+                if (type_is_invalid(cast1->value.type))
+                    return ira->codegen->invalid_instruction;
+                return ir_analyze_maybe_wrap(ira, source_instr, cast1, wanted_type);
+            }
         }
     }
 
@@ -10963,7 +11036,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
 
     // cast from union to the enum type of the union
     if (actual_type->id == ZigTypeIdUnion && wanted_type->id == ZigTypeIdEnum) {
-        if ((err = type_ensure_zero_bits_known(ira->codegen, actual_type)))
+        if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown)))
             return ira->codegen->invalid_instruction;
 
         if (actual_type->data.unionation.tag_type == wanted_type) {
@@ -10976,7 +11049,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
         wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
     {
-        if ((err = type_ensure_zero_bits_known(ira->codegen, wanted_type)))
+        if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown)))
             return ira->codegen->invalid_instruction;
 
         if (wanted_type->data.unionation.tag_type == actual_type) {
@@ -10990,7 +11063,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||
             union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)
         {
-            if ((err = type_ensure_zero_bits_known(ira->codegen, union_type)))
+            if ((err = type_resolve(ira->codegen, union_type, ResolveStatusZeroBitsKnown)))
                 return ira->codegen->invalid_instruction;
 
             if (union_type->data.unionation.tag_type == actual_type) {
@@ -11017,14 +11090,24 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
             actual_type->data.pointer.child_type, source_node,
             !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
         {
-            if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) {
+            if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type,
+                            ResolveStatusAlignmentKnown)))
+            {
+                return ira->codegen->invalid_instruction;
+            }
+            if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type,
+                            ResolveStatusAlignmentKnown)))
+            {
+                return ira->codegen->invalid_instruction;
+            }
+            uint32_t wanted_align = get_ptr_align(ira->codegen, wanted_type);
+            uint32_t actual_align = get_ptr_align(ira->codegen, actual_type);
+            if (wanted_align > actual_align) {
                 ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));
                 add_error_note(ira->codegen, msg, value->source_node,
-                        buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name),
-                            actual_type->data.pointer.alignment));
+                        buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), actual_align));
                 add_error_note(ira->codegen, msg, source_instr->source_node,
-                        buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name),
-                            wanted_type->data.pointer.alignment));
+                        buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), wanted_align));
                 return ira->codegen->invalid_instruction;
             }
             return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type);
@@ -11036,7 +11119,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
             actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
     {
-        if ((err = type_ensure_zero_bits_known(ira->codegen, actual_type))) {
+        if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) {
             return ira->codegen->invalid_instruction;
         }
         if (!type_has_bits(actual_type)) {
@@ -11282,8 +11365,7 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
         return nullptr;
 
     ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
-            true, false, PtrLenUnknown,
-            get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0);
+            true, false, PtrLenUnknown, 0, 0, 0);
     ZigType *str_type = get_slice_type(ira->codegen, ptr_type);
     IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type);
     if (type_is_invalid(casted_value->value.type))
@@ -11573,8 +11655,6 @@ static ZigType *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op
     ZigType *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2);
     if (type_is_invalid(resolved_type))
         return resolved_type;
-    if ((err = type_ensure_zero_bits_known(ira->codegen, resolved_type)))
-        return resolved_type;
 
     bool operator_allowed;
     switch (resolved_type->id) {
@@ -11630,6 +11710,9 @@ static ZigType *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op
     if (casted_op2 == ira->codegen->invalid_instruction)
         return ira->codegen->builtin_types.entry_invalid;
 
+    if ((err = type_resolve(ira->codegen, resolved_type, ResolveStatusZeroBitsKnown)))
+        return resolved_type;
+
     bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type);
     if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) {
         ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad);
@@ -12316,7 +12399,7 @@ static ZigType *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruc
         out_array_val = out_val;
     } else if (is_slice(op1_type) || is_slice(op2_type)) {
         ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, child_type,
-                true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, child_type), 0, 0);
+                true, false, PtrLenUnknown, 0, 0, 0);
         result_type = get_slice_type(ira->codegen, ptr_type);
         out_array_val = create_const_vals(1);
         out_array_val->special = ConstValSpecialStatic;
@@ -12337,8 +12420,7 @@ static ZigType *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruc
         new_len += 1; // null byte
 
         // TODO make this `[*]null T` instead of `[*]T`
-        result_type = get_pointer_to_type_extra(ira->codegen, child_type, true, false,
-                PtrLenUnknown, get_abi_alignment(ira->codegen, child_type), 0, 0);
+        result_type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, PtrLenUnknown, 0, 0, 0);
 
         out_array_val = create_const_vals(1);
         out_array_val->special = ConstValSpecialStatic;
@@ -12563,7 +12645,7 @@ static ZigType *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDec
     if (type_is_invalid(result_type)) {
         result_type = ira->codegen->builtin_types.entry_invalid;
     } else {
-        if ((err = type_ensure_zero_bits_known(ira->codegen, result_type))) {
+        if ((err = type_resolve(ira->codegen, result_type, ResolveStatusZeroBitsKnown))) {
             result_type = ira->codegen->builtin_types.entry_invalid;
         }
     }
@@ -12631,6 +12713,11 @@ static ZigType *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDec
     }
 
     if (decl_var_instruction->align_value == nullptr) {
+        if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) {
+            var->value->type = ira->codegen->builtin_types.entry_invalid;
+            decl_var_instruction->base.other = &decl_var_instruction->base;
+            return ira->codegen->builtin_types.entry_void;
+        }
         var->align_bytes = get_abi_alignment(ira->codegen, result_type);
     } else {
         if (!ir_resolve_align(ira, decl_var_instruction->align_value->other, &var->align_bytes)) {
@@ -13100,7 +13187,6 @@ static ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) {
 static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
         ZigVar *var)
 {
-    Error err;
     while (var->next_var != nullptr) {
         var = var->next_var;
     }
@@ -13158,8 +13244,6 @@ no_mem_slot:
             instruction->scope, instruction->source_node, var);
     var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type,
             var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0);
-    if ((err = type_ensure_zero_bits_known(ira->codegen, var->value->type)))
-        return ira->codegen->invalid_instruction;
 
     bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
     var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
@@ -13356,8 +13440,7 @@ static ZigType *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instr
     IrInstruction *casted_new_stack = nullptr;
     if (call_instruction->new_stack != nullptr) {
         ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
-                false, false, PtrLenUnknown,
-                get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0);
+                false, false, PtrLenUnknown, 0, 0, 0);
         ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
         IrInstruction *new_stack = call_instruction->new_stack->other;
         if (type_is_invalid(new_stack->value.type))
@@ -13536,7 +13619,7 @@ static ZigType *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instr
                 inst_fn_type_id.return_type = specified_return_type;
             }
 
-            if ((err = type_ensure_zero_bits_known(ira->codegen, specified_return_type)))
+            if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusZeroBitsKnown)))
                 return ira->codegen->builtin_types.entry_invalid;
 
             if (type_requires_comptime(specified_return_type)) {
@@ -14212,7 +14295,7 @@ static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) {
             ptr_type->data.pointer.child_type,
             ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
             ptr_len,
-            ptr_type->data.pointer.alignment,
+            ptr_type->data.pointer.explicit_alignment,
             ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count);
 }
 
@@ -14264,7 +14347,7 @@ static ZigType *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionEle
             return_type = get_pointer_to_type_extra(ira->codegen, child_type,
                     ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
                     elem_ptr_instruction->ptr_len,
-                    ptr_type->data.pointer.alignment, 0, 0);
+                    ptr_type->data.pointer.explicit_alignment, 0, 0);
         } else {
             uint64_t elem_val_scalar;
             if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar))
@@ -14336,7 +14419,7 @@ static ZigType *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionEle
 
     uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type);
     uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type);
-    uint64_t ptr_align = return_type->data.pointer.alignment;
+    uint64_t ptr_align = get_ptr_align(ira->codegen, return_type);
     if (instr_is_comptime(casted_elem_index)) {
         uint64_t index = bigint_as_unsigned(&casted_elem_index->value.data.x_bigint);
         if (array_type->id == ZigTypeIdArray) {
@@ -14653,9 +14736,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
                     }
 
                     ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
-                            is_const, is_volatile,
-                            PtrLenSingle,
-                            get_abi_alignment(ira->codegen, field_type), 0, 0);
+                            is_const, is_volatile, PtrLenSingle, 0, 0, 0);
 
                     IrInstruction *result = ir_get_const(ira, source_instr);
                     ConstExprValue *const_val = &result->value;
@@ -14669,7 +14750,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
 
             IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field);
             result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile,
-                    PtrLenSingle, get_abi_alignment(ira->codegen, field->type_entry), 0, 0);
+                    PtrLenSingle, 0, 0, 0);
             return result;
         } else {
             return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
@@ -15002,9 +15083,14 @@ static ZigType *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFi
             } else if (buf_eql_str(field_name, "alignment")) {
                 bool ptr_is_const = true;
                 bool ptr_is_volatile = false;
+                if ((err = type_resolve(ira->codegen, child_type->data.pointer.child_type,
+                                ResolveStatusAlignmentKnown)))
+                {
+                    return ira->codegen->builtin_types.entry_invalid;
+                }
                 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
                     create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
-                        child_type->data.pointer.alignment, false),
+                        get_ptr_align(ira->codegen, child_type), false),
                     ira->codegen->builtin_types.entry_num_lit_int,
                     ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
             } else {
@@ -15461,7 +15547,7 @@ static ZigType *ir_analyze_instruction_slice_type(IrAnalyze *ira,
         IrInstructionSliceType *slice_type_instruction)
 {
     Error err;
-    uint32_t align_bytes;
+    uint32_t align_bytes = 0;
     if (slice_type_instruction->align_value != nullptr) {
         if (!ir_resolve_align(ira, slice_type_instruction->align_value->other, &align_bytes))
             return ira->codegen->builtin_types.entry_invalid;
@@ -15471,12 +15557,6 @@ static ZigType *ir_analyze_instruction_slice_type(IrAnalyze *ira,
     if (type_is_invalid(child_type))
         return ira->codegen->builtin_types.entry_invalid;
 
-    if (slice_type_instruction->align_value == nullptr) {
-        if ((err = type_ensure_zero_bits_known(ira->codegen, child_type)))
-            return ira->codegen->builtin_types.entry_invalid;
-        align_bytes = get_abi_alignment(ira->codegen, child_type);
-    }
-
     bool is_const = slice_type_instruction->is_const;
     bool is_volatile = slice_type_instruction->is_volatile;
 
@@ -15511,7 +15591,7 @@ static ZigType *ir_analyze_instruction_slice_type(IrAnalyze *ira,
         case ZigTypeIdBoundFn:
         case ZigTypeIdPromise:
             {
-                if ((err = type_ensure_zero_bits_known(ira->codegen, child_type)))
+                if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown)))
                     return ira->codegen->builtin_types.entry_invalid;
                 ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type,
                         is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0);
@@ -15751,9 +15831,7 @@ static ZigType *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
     }
     ZigType *child_type = type_entry->data.maybe.child_type;
     ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type,
-            ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
-            PtrLenSingle,
-            get_abi_alignment(ira->codegen, child_type), 0, 0);
+            ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0);
 
     if (instr_is_comptime(value)) {
         ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
@@ -16123,7 +16201,7 @@ static ZigType *ir_analyze_instruction_switch_target(IrAnalyze *ira,
             return tag_type;
         }
         case ZigTypeIdEnum: {
-            if ((err = type_ensure_zero_bits_known(ira->codegen, target_type)))
+            if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))
                 return ira->codegen->builtin_types.entry_invalid;
             if (target_type->data.enumeration.src_field_count < 2) {
                 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
@@ -16352,7 +16430,7 @@ static ZigType *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruc
     if (casted_field_value == ira->codegen->invalid_instruction)
         return ira->codegen->builtin_types.entry_invalid;
 
-    if ((err = type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type)))
+    if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown)))
         return ira->codegen->builtin_types.entry_invalid;
 
     bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);
@@ -16752,7 +16830,7 @@ static ZigType *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstructionErr
         return ira->codegen->builtin_types.entry_invalid;
 
     ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
-            true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0);
+            true, false, PtrLenUnknown, 0, 0, 0);
     ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type);
     if (casted_value->value.special == ConstValSpecialStatic) {
         ErrorTableEntry *err = casted_value->value.data.x_err_set;
@@ -16779,7 +16857,7 @@ static ZigType *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructi
     assert(target->value.type->id == ZigTypeIdEnum);
 
     if (instr_is_comptime(target)) {
-        if ((err = type_ensure_zero_bits_known(ira->codegen, target->value.type)))
+        if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusZeroBitsKnown)))
             return ira->codegen->builtin_types.entry_invalid;
         TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint);
         ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name);
@@ -16794,8 +16872,7 @@ static ZigType *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructi
     ZigType *u8_ptr_type = get_pointer_to_type_extra(
             ira->codegen, ira->codegen->builtin_types.entry_u8,
             true, false, PtrLenUnknown,
-            get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8),
-            0, 0);
+            0, 0, 0);
     result->value.type = get_slice_type(ira->codegen, u8_ptr_type);
     return result->value.type;
 }
@@ -17158,8 +17235,7 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco
                     ZigType *u8_ptr = get_pointer_to_type_extra(
                         ira->codegen, ira->codegen->builtin_types.entry_u8,
                         true, false, PtrLenUnknown,
-                        get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8),
-                        0, 0);
+                        0, 0, 0);
                     fn_def_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
                     if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) {
                         fn_def_fields[6].data.x_optional = create_const_vals(1);
@@ -17279,7 +17355,7 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty
     ensure_field_index(result->type, "alignment", 3);
     fields[3].special = ConstValSpecialStatic;
     fields[3].type = get_int_type(ira->codegen, false, 29);
-    bigint_init_unsigned(&fields[3].data.x_bigint, attrs_type->data.pointer.alignment);
+    bigint_init_unsigned(&fields[3].data.x_bigint, get_ptr_align(ira->codegen, attrs_type));
     // child: type
     ensure_field_index(result->type, "child", 4);
     fields[4].special = ConstValSpecialStatic;
@@ -18369,7 +18445,21 @@ static ZigType *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructio
     return dest_type;
 }
 
+static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) {
+    Error err;
+
+    if (ty->id == ZigTypeIdPointer) {
+        if ((err = type_resolve(ira->codegen, ty->data.pointer.child_type, ResolveStatusAlignmentKnown)))
+            return err;
+    }
+
+    *result_align = get_ptr_align(ira->codegen, ty);
+    return ErrorNone;
+}
+
 static ZigType *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) {
+    Error err;
+
     ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->other);
     if (type_is_invalid(dest_child_type))
         return ira->codegen->builtin_types.entry_invalid;
@@ -18384,15 +18474,23 @@ static ZigType *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionF
     if (target->value.type->id == ZigTypeIdPointer) {
         src_ptr_const = target->value.type->data.pointer.is_const;
         src_ptr_volatile = target->value.type->data.pointer.is_volatile;
-        src_ptr_align = target->value.type->data.pointer.alignment;
+
+        if ((err = resolve_ptr_align(ira, target->value.type, &src_ptr_align)))
+            return ira->codegen->builtin_types.entry_invalid;
     } else if (is_slice(target->value.type)) {
         ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry;
         src_ptr_const = src_ptr_type->data.pointer.is_const;
         src_ptr_volatile = src_ptr_type->data.pointer.is_volatile;
-        src_ptr_align = src_ptr_type->data.pointer.alignment;
+
+        if ((err = resolve_ptr_align(ira, src_ptr_type, &src_ptr_align)))
+            return ira->codegen->builtin_types.entry_invalid;
     } else {
         src_ptr_const = true;
         src_ptr_volatile = false;
+
+        if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusAlignmentKnown)))
+            return ira->codegen->builtin_types.entry_invalid;
+
         src_ptr_align = get_abi_alignment(ira->codegen, target->value.type);
     }
 
@@ -18450,6 +18548,8 @@ static ZigType *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionF
 }
 
 static ZigType *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) {
+    Error err;
+
     IrInstruction *target = instruction->target->other;
     if (type_is_invalid(target->value.type))
         return ira->codegen->builtin_types.entry_invalid;
@@ -18462,9 +18562,13 @@ static ZigType *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToB
 
     ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry;
 
+    uint32_t alignment;
+    if ((err = resolve_ptr_align(ira, src_ptr_type, &alignment)))
+        return ira->codegen->builtin_types.entry_invalid;
+
     ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
             src_ptr_type->data.pointer.is_const, src_ptr_type->data.pointer.is_volatile, PtrLenUnknown,
-            src_ptr_type->data.pointer.alignment, 0, 0);
+            alignment, 0, 0);
     ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type);
 
     IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true);
@@ -18622,6 +18726,8 @@ static ZigType *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoo
 }
 
 static ZigType *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) {
+    Error err;
+
     IrInstruction *dest_ptr = instruction->dest_ptr->other;
     if (type_is_invalid(dest_ptr->value.type))
         return ira->codegen->builtin_types.entry_invalid;
@@ -18640,8 +18746,13 @@ static ZigType *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemse
 
     ZigType *usize = ira->codegen->builtin_types.entry_usize;
     ZigType *u8 = ira->codegen->builtin_types.entry_u8;
-    uint32_t dest_align = (dest_uncasted_type->id == ZigTypeIdPointer) ?
-        dest_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8);
+    uint32_t dest_align;
+    if (dest_uncasted_type->id == ZigTypeIdPointer) {
+        if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align)))
+            return ira->codegen->builtin_types.entry_invalid;
+    } else {
+        dest_align = get_abi_alignment(ira->codegen, u8);
+    }
     ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile,
             PtrLenUnknown, dest_align, 0, 0);
 
@@ -18714,6 +18825,8 @@ static ZigType *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemse
 }
 
 static ZigType *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcpy *instruction) {
+    Error err;
+
     IrInstruction *dest_ptr = instruction->dest_ptr->other;
     if (type_is_invalid(dest_ptr->value.type))
         return ira->codegen->builtin_types.entry_invalid;
@@ -18733,10 +18846,22 @@ static ZigType *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcp
         dest_uncasted_type->data.pointer.is_volatile;
     bool src_is_volatile = (src_uncasted_type->id == ZigTypeIdPointer) &&
         src_uncasted_type->data.pointer.is_volatile;
-    uint32_t dest_align = (dest_uncasted_type->id == ZigTypeIdPointer) ?
-        dest_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8);
-    uint32_t src_align = (src_uncasted_type->id == ZigTypeIdPointer) ?
-        src_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8);
+
+    uint32_t dest_align;
+    if (dest_uncasted_type->id == ZigTypeIdPointer) {
+        if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align)))
+            return ira->codegen->builtin_types.entry_invalid;
+    } else {
+        dest_align = get_abi_alignment(ira->codegen, u8);
+    }
+
+    uint32_t src_align;
+    if (src_uncasted_type->id == ZigTypeIdPointer) {
+        if ((err = resolve_ptr_align(ira, src_uncasted_type, &src_align)))
+            return ira->codegen->builtin_types.entry_invalid;
+    } else {
+        src_align = get_abi_alignment(ira->codegen, u8);
+    }
 
     ZigType *usize = ira->codegen->builtin_types.entry_usize;
     ZigType *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile,
@@ -18881,17 +19006,13 @@ static ZigType *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice
     ZigType *return_type;
 
     if (array_type->id == ZigTypeIdArray) {
-        uint32_t byte_alignment = ptr_type->data.pointer.alignment;
-        if (array_type->data.array.len == 0 && byte_alignment == 0) {
-            byte_alignment = get_abi_alignment(ira->codegen, array_type->data.array.child_type);
-        }
         bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic &&
             ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst;
         ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type,
             ptr_type->data.pointer.is_const || is_comptime_const,
             ptr_type->data.pointer.is_volatile,
             PtrLenUnknown,
-            byte_alignment, 0, 0);
+            ptr_type->data.pointer.explicit_alignment, 0, 0);
         return_type = get_slice_type(ira->codegen, slice_ptr_type);
     } else if (array_type->id == ZigTypeIdPointer) {
         if (array_type->data.pointer.ptr_len == PtrLenSingle) {
@@ -18901,7 +19022,7 @@ static ZigType *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice
                         main_type->data.pointer.child_type,
                         array_type->data.pointer.is_const, array_type->data.pointer.is_volatile,
                         PtrLenUnknown,
-                        array_type->data.pointer.alignment, 0, 0);
+                        array_type->data.pointer.explicit_alignment, 0, 0);
                 return_type = get_slice_type(ira->codegen, slice_ptr_type);
             } else {
                 ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer"));
@@ -18911,7 +19032,7 @@ static ZigType *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice
             ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type,
                     array_type->data.pointer.is_const, array_type->data.pointer.is_volatile,
                     PtrLenUnknown,
-                    array_type->data.pointer.alignment, 0, 0);
+                    array_type->data.pointer.explicit_alignment, 0, 0);
             return_type = get_slice_type(ira->codegen, slice_ptr_type);
             if (!end) {
                 ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value"));
@@ -19292,7 +19413,7 @@ static ZigType *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAli
         return ira->codegen->builtin_types.entry_invalid;
     ZigType *type_entry = ir_resolve_type(ira, type_value);
 
-    if ((err = type_ensure_zero_bits_known(ira->codegen, type_entry)))
+    if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusAlignmentKnown)))
         return ira->codegen->builtin_types.entry_invalid;
 
     switch (type_entry->id) {
@@ -19336,6 +19457,8 @@ static ZigType *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAli
 }
 
 static ZigType *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstructionOverflowOp *instruction) {
+    Error err;
+
     IrInstruction *type_value = instruction->type_value->other;
     if (type_is_invalid(type_value->value.type))
         return ira->codegen->builtin_types.entry_invalid;
@@ -19379,10 +19502,13 @@ static ZigType *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstruction
 
     ZigType *expected_ptr_type;
     if (result_ptr->value.type->id == ZigTypeIdPointer) {
+        uint32_t alignment;
+        if ((err = resolve_ptr_align(ira, result_ptr->value.type, &alignment)))
+            return ira->codegen->builtin_types.entry_invalid;
         expected_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_type,
                 false, result_ptr->value.type->data.pointer.is_volatile,
                 PtrLenSingle,
-                result_ptr->value.type->data.pointer.alignment, 0, 0);
+                alignment, 0, 0);
     } else {
         expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false);
     }
@@ -19544,8 +19670,7 @@ static ZigType *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
         }
         ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type,
                 ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
-                PtrLenSingle,
-                get_abi_alignment(ira->codegen, payload_type), 0, 0);
+                PtrLenSingle, 0, 0, 0);
         if (instr_is_comptime(value)) {
             ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
             if (!ptr_val)
@@ -19624,7 +19749,7 @@ static ZigType *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnP
             ZigType *param_type = ir_resolve_type(ira, param_type_value);
             if (type_is_invalid(param_type))
                 return ira->codegen->builtin_types.entry_invalid;
-            if ((err = type_ensure_zero_bits_known(ira->codegen, param_type)))
+            if ((err = type_resolve(ira->codegen, param_type, ResolveStatusZeroBitsKnown)))
                 return ira->codegen->builtin_types.entry_invalid;
             if (type_requires_comptime(param_type)) {
                 if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
@@ -19899,7 +20024,7 @@ static ZigType *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic
     }
 
     ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
-            true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0);
+            true, false, PtrLenUnknown, 0, 0, 0);
     ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type);
     IrInstruction *casted_msg = ir_implicit_cast(ira, msg, str_type);
     if (type_is_invalid(casted_msg->value.type))
@@ -19912,6 +20037,8 @@ static ZigType *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic
 }
 
 static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint32_t align_bytes, bool safety_check_on) {
+    Error err;
+
     ZigType *target_type = target->value.type;
     assert(!type_is_invalid(target_type));
 
@@ -19920,7 +20047,8 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
 
     if (target_type->id == ZigTypeIdPointer) {
         result_type = adjust_ptr_align(ira->codegen, target_type, align_bytes);
-        old_align_bytes = target_type->data.pointer.alignment;
+        if ((err = resolve_ptr_align(ira, target_type, &old_align_bytes)))
+            return ira->codegen->invalid_instruction;
     } else if (target_type->id == ZigTypeIdFn) {
         FnTypeId fn_type_id = target_type->data.fn.fn_type_id;
         old_align_bytes = fn_type_id.alignment;
@@ -19930,7 +20058,8 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
             target_type->data.maybe.child_type->id == ZigTypeIdPointer)
     {
         ZigType *ptr_type = target_type->data.maybe.child_type;
-        old_align_bytes = ptr_type->data.pointer.alignment;
+        if ((err = resolve_ptr_align(ira, ptr_type, &old_align_bytes)))
+            return ira->codegen->invalid_instruction;
         ZigType *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes);
 
         result_type = get_optional_type(ira->codegen, better_ptr_type);
@@ -19944,7 +20073,8 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
         result_type = get_optional_type(ira->codegen, fn_type);
     } else if (is_slice(target_type)) {
         ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry;
-        old_align_bytes = slice_ptr_type->data.pointer.alignment;
+        if ((err = resolve_ptr_align(ira, slice_ptr_type, &old_align_bytes)))
+            return ira->codegen->invalid_instruction;
         ZigType *result_ptr_type = adjust_ptr_align(ira->codegen, slice_ptr_type, align_bytes);
         result_type = get_slice_type(ira->codegen, result_ptr_type);
     } else {
@@ -20023,8 +20153,13 @@ static ZigType *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtr
         return dest_type;
     }
 
-    uint32_t src_align_bytes = get_ptr_align(ira->codegen, src_type);
-    uint32_t dest_align_bytes = get_ptr_align(ira->codegen, dest_type);
+    uint32_t src_align_bytes;
+    if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes)))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    uint32_t dest_align_bytes;
+    if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes)))
+        return ira->codegen->builtin_types.entry_invalid;
 
     if (dest_align_bytes > src_align_bytes) {
         ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("cast increases pointer alignment"));
@@ -20041,7 +20176,7 @@ static ZigType *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtr
 
     // Keep the bigger alignment, it can only help-
     // unless the target is zero bits.
-    if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type)))
+    if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
         return ira->codegen->builtin_types.entry_invalid;
 
     IrInstruction *result;
@@ -20289,7 +20424,7 @@ static ZigType *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionI
         return ira->codegen->builtin_types.entry_invalid;
     }
 
-    if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type)))
+    if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
         return ira->codegen->builtin_types.entry_invalid;
     if (!type_has_bits(dest_type)) {
         ir_add_error(ira, dest_type_value,
@@ -20440,12 +20575,15 @@ static ZigType *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtr
     if (instruction->align_value != nullptr) {
         if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes))
             return ira->codegen->builtin_types.entry_invalid;
+        if ((err = type_resolve(ira->codegen, child_type, ResolveStatusAlignmentKnown)))
+            return ira->codegen->builtin_types.entry_invalid;
     } else {
-        if ((err = type_ensure_zero_bits_known(ira->codegen, child_type)))
+        if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown)))
             return ira->codegen->builtin_types.entry_invalid;
-        align_bytes = get_abi_alignment(ira->codegen, child_type);
+        align_bytes = 0;
     }
 
+
     ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
     out_val->data.x_type = get_pointer_to_type_extra(ira->codegen, child_type,
             instruction->is_const, instruction->is_volatile,
@@ -21089,7 +21227,7 @@ static ZigType *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstruction
         return ira->codegen->builtin_types.entry_invalid;
     }
 
-    if ((err = type_ensure_zero_bits_known(ira->codegen, target->value.type)))
+    if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusZeroBitsKnown)))
         return ira->codegen->builtin_types.entry_invalid;
 
     ZigType *tag_type = target->value.type->data.enumeration.tag_int_type;
@@ -21112,7 +21250,7 @@ static ZigType *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstruction
         return ira->codegen->builtin_types.entry_invalid;
     }
 
-    if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type)))
+    if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
         return ira->codegen->builtin_types.entry_invalid;
 
     ZigType *tag_type = dest_type->data.enumeration.tag_int_type;
std/debug/index.zig
@@ -916,7 +916,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
     } else {
         return error.MissingDebugInfo;
     };
-    const syms = @ptrCast([*]macho.nlist_64, hdr_base + symtab.symoff)[0..symtab.nsyms];
+    const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms];
     const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize];
 
     const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);
@@ -1497,14 +1497,14 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
         const segcmd = while (ncmd != 0) : (ncmd -= 1) {
             const lc = @ptrCast(*const std.macho.load_command, ptr);
             switch (lc.cmd) {
-                std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, ptr),
+                std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)),
                 else => {},
             }
             ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403
         } else {
             return error.MissingDebugInfo;
         };
-        const sections = @alignCast(@alignOf(macho.section_64), @ptrCast([*]const macho.section_64, ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects];
+        const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects];
         for (sections) |*sect| {
             if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and
                 (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG)
test/cases/align.zig
@@ -212,3 +212,10 @@ fn fnWithAlignedStack() i32 {
     @setAlignStack(256);
     return 1234;
 }
+
+test "alignment of structs" {
+    assert(@alignOf(struct {
+        a: i32,
+        b: *i32,
+    }) == @alignOf(usize));
+}
test/compile_errors.zig
@@ -3444,7 +3444,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\
         \\export fn entry() usize { return @sizeOf(@typeOf(foo)); }
     ,
-        ".tmp_source.zig:8:26: error: expected type '*const u3', found '*align(1:3:6) const u3'",
+        ".tmp_source.zig:8:26: error: expected type '*const u3', found '*align(:3:6) const u3'",
     );
 
     cases.add(