Commit 9b2345e182

Jacob Young <jacobly0@users.noreply.github.com>
2024-03-18 15:00:27
Sema: rework `@fieldParentPtr` to accept a pointer type
There is no way to know the expected parent pointer attributes (most notably alignment) from the type of the field pointer, so provide them in the first argument.
1 parent 5a41704
src/arch/x86_64/CodeGen.zig
@@ -7920,17 +7920,14 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32
     const mod = self.bin_file.comp.module.?;
     const ptr_field_ty = self.typeOfIndex(inst);
     const ptr_container_ty = self.typeOf(operand);
-    const ptr_container_ty_info = ptr_container_ty.ptrInfo(mod);
     const container_ty = ptr_container_ty.childType(mod);
 
-    const field_offset: i32 = if (mod.typeToPackedStruct(container_ty)) |struct_obj|
-        if (ptr_field_ty.ptrInfo(mod).packed_offset.host_size == 0)
-            @divExact(mod.structPackedFieldBitOffset(struct_obj, index) +
-                ptr_container_ty_info.packed_offset.bit_offset, 8)
-        else
-            0
-    else
-        @intCast(container_ty.structFieldOffset(index, mod));
+    const field_off: i32 = switch (container_ty.containerLayout(mod)) {
+        .auto, .@"extern" => @intCast(container_ty.structFieldOffset(index, mod)),
+        .@"packed" => @divExact(@as(i32, ptr_container_ty.ptrInfo(mod).packed_offset.bit_offset) +
+            (if (mod.typeToStruct(container_ty)) |struct_obj| mod.structPackedFieldBitOffset(struct_obj, index) else 0) -
+            ptr_field_ty.ptrInfo(mod).packed_offset.bit_offset, 8),
+    };
 
     const src_mcv = try self.resolveInst(operand);
     const dst_mcv = if (switch (src_mcv) {
@@ -7938,7 +7935,7 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32
         .register, .register_offset => self.reuseOperand(inst, operand, 0, src_mcv),
         else => false,
     }) src_mcv else try self.copyToRegisterWithInstTracking(inst, ptr_field_ty, src_mcv);
-    return dst_mcv.offset(field_offset);
+    return dst_mcv.offset(field_off);
 }
 
 fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
@@ -7958,11 +7955,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
 
         const src_mcv = try self.resolveInst(operand);
         const field_off: u32 = switch (container_ty.containerLayout(mod)) {
-            .auto, .@"extern" => @intCast(container_ty.structFieldOffset(index, mod) * 8),
-            .@"packed" => if (mod.typeToStruct(container_ty)) |struct_type|
-                mod.structPackedFieldBitOffset(struct_type, index)
-            else
-                0,
+            .auto, .@"extern" => @intCast(container_ty.structFieldOffset(extra.field_index, mod) * 8),
+            .@"packed" => if (mod.typeToStruct(container_ty)) |struct_obj| mod.structPackedFieldBitOffset(struct_obj, extra.field_index) else 0,
         };
 
         switch (src_mcv) {
@@ -8239,7 +8233,12 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
 
     const inst_ty = self.typeOfIndex(inst);
     const parent_ty = inst_ty.childType(mod);
-    const field_offset: i32 = @intCast(parent_ty.structFieldOffset(extra.field_index, mod));
+    const field_off: i32 = switch (parent_ty.containerLayout(mod)) {
+        .auto, .@"extern" => @intCast(parent_ty.structFieldOffset(extra.field_index, mod)),
+        .@"packed" => @divExact(@as(i32, inst_ty.ptrInfo(mod).packed_offset.bit_offset) +
+            (if (mod.typeToStruct(parent_ty)) |struct_obj| mod.structPackedFieldBitOffset(struct_obj, extra.field_index) else 0) -
+            self.typeOf(extra.field_ptr).ptrInfo(mod).packed_offset.bit_offset, 8),
+    };
 
     const src_mcv = try self.resolveInst(extra.field_ptr);
     const dst_mcv = if (src_mcv.isRegisterOffset() and
@@ -8247,7 +8246,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
         src_mcv
     else
         try self.copyToRegisterWithInstTracking(inst, inst_ty, src_mcv);
-    const result = dst_mcv.offset(-field_offset);
+    const result = dst_mcv.offset(-field_off);
     return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none });
 }
 
@@ -17950,7 +17949,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
             .Struct => {
                 const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(result_ty, mod));
                 if (result_ty.containerLayout(mod) == .@"packed") {
-                    const struct_type = mod.typeToStruct(result_ty).?;
+                    const struct_obj = mod.typeToStruct(result_ty).?;
                     try self.genInlineMemset(
                         .{ .lea_frame = .{ .index = frame_index } },
                         .{ .immediate = 0 },
@@ -17971,7 +17970,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
                         }
                         const elem_abi_size: u32 = @intCast(elem_ty.abiSize(mod));
                         const elem_abi_bits = elem_abi_size * 8;
-                        const elem_off = mod.structPackedFieldBitOffset(struct_type, elem_i);
+                        const elem_off = mod.structPackedFieldBitOffset(struct_obj, elem_i);
                         const elem_byte_off: i32 = @intCast(elem_off / elem_abi_bits * elem_abi_size);
                         const elem_bit_off = elem_off % elem_abi_bits;
                         const elem_mcv = try self.resolveInst(elem);
src/InternPool.zig
@@ -5163,48 +5163,55 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
         .ptr => |ptr| {
             const ptr_type = ip.indexToKey(ptr.ty).ptr_type;
             assert(ptr_type.flags.size != .Slice);
-            switch (ptr.addr) {
-                .decl => |decl| ip.items.appendAssumeCapacity(.{
+            ip.items.appendAssumeCapacity(switch (ptr.addr) {
+                .decl => |decl| .{
                     .tag = .ptr_decl,
                     .data = try ip.addExtra(gpa, PtrDecl{
                         .ty = ptr.ty,
                         .decl = decl,
                     }),
-                }),
-                .comptime_alloc => |alloc_index| ip.items.appendAssumeCapacity(.{
+                },
+                .comptime_alloc => |alloc_index| .{
                     .tag = .ptr_comptime_alloc,
                     .data = try ip.addExtra(gpa, PtrComptimeAlloc{
                         .ty = ptr.ty,
                         .index = alloc_index,
                     }),
-                }),
-                .anon_decl => |anon_decl| ip.items.appendAssumeCapacity(
-                    if (ptrsHaveSameAlignment(ip, ptr.ty, ptr_type, anon_decl.orig_ty)) .{
+                },
+                .anon_decl => |anon_decl| if (ptrsHaveSameAlignment(ip, ptr.ty, ptr_type, anon_decl.orig_ty)) item: {
+                    if (ptr.ty != anon_decl.orig_ty) {
+                        _ = ip.map.pop();
+                        var new_key = key;
+                        new_key.ptr.addr.anon_decl.orig_ty = ptr.ty;
+                        const new_gop = try ip.map.getOrPutAdapted(gpa, new_key, adapter);
+                        if (new_gop.found_existing) return @enumFromInt(new_gop.index);
+                    }
+                    break :item .{
                         .tag = .ptr_anon_decl,
                         .data = try ip.addExtra(gpa, PtrAnonDecl{
                             .ty = ptr.ty,
                             .val = anon_decl.val,
                         }),
-                    } else .{
-                        .tag = .ptr_anon_decl_aligned,
-                        .data = try ip.addExtra(gpa, PtrAnonDeclAligned{
-                            .ty = ptr.ty,
-                            .val = anon_decl.val,
-                            .orig_ty = anon_decl.orig_ty,
-                        }),
-                    },
-                ),
-                .comptime_field => |field_val| {
+                    };
+                } else .{
+                    .tag = .ptr_anon_decl_aligned,
+                    .data = try ip.addExtra(gpa, PtrAnonDeclAligned{
+                        .ty = ptr.ty,
+                        .val = anon_decl.val,
+                        .orig_ty = anon_decl.orig_ty,
+                    }),
+                },
+                .comptime_field => |field_val| item: {
                     assert(field_val != .none);
-                    ip.items.appendAssumeCapacity(.{
+                    break :item .{
                         .tag = .ptr_comptime_field,
                         .data = try ip.addExtra(gpa, PtrComptimeField{
                             .ty = ptr.ty,
                             .field_val = field_val,
                         }),
-                    });
+                    };
                 },
-                .int, .eu_payload, .opt_payload => |base| {
+                .int, .eu_payload, .opt_payload => |base| item: {
                     switch (ptr.addr) {
                         .int => assert(ip.typeOf(base) == .usize_type),
                         .eu_payload => assert(ip.indexToKey(
@@ -5215,7 +5222,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                         ) == .opt_type),
                         else => unreachable,
                     }
-                    ip.items.appendAssumeCapacity(.{
+                    break :item .{
                         .tag = switch (ptr.addr) {
                             .int => .ptr_int,
                             .eu_payload => .ptr_eu_payload,
@@ -5226,9 +5233,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                             .ty = ptr.ty,
                             .base = base,
                         }),
-                    });
+                    };
                 },
-                .elem, .field => |base_index| {
+                .elem, .field => |base_index| item: {
                     const base_ptr_type = ip.indexToKey(ip.typeOf(base_index.base)).ptr_type;
                     switch (ptr.addr) {
                         .elem => assert(base_ptr_type.flags.size == .Many),
@@ -5265,7 +5272,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                     } });
                     assert(!(try ip.map.getOrPutAdapted(gpa, key, adapter)).found_existing);
                     try ip.items.ensureUnusedCapacity(gpa, 1);
-                    ip.items.appendAssumeCapacity(.{
+                    break :item .{
                         .tag = switch (ptr.addr) {
                             .elem => .ptr_elem,
                             .field => .ptr_field,
@@ -5276,9 +5283,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                             .base = base_index.base,
                             .index = index_index,
                         }),
-                    });
+                    };
                 },
-            }
+            });
         },
 
         .opt => |opt| {
src/Sema.zig
@@ -17699,19 +17699,20 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     .ty = new_decl_ty.toIntern(),
                     .storage = .{ .elems = param_vals },
                 } });
-                const ptr_ty = (try sema.ptrType(.{
+                const slice_ty = (try sema.ptrType(.{
                     .child = param_info_ty.toIntern(),
                     .flags = .{
                         .size = .Slice,
                         .is_const = true,
                     },
                 })).toIntern();
+                const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(mod).toIntern();
                 break :v try mod.intern(.{ .slice = .{
-                    .ty = ptr_ty,
+                    .ty = slice_ty,
                     .ptr = try mod.intern(.{ .ptr = .{
-                        .ty = Type.fromInterned(ptr_ty).slicePtrFieldType(mod).toIntern(),
+                        .ty = manyptr_ty,
                         .addr = .{ .anon_decl = .{
-                            .orig_ty = ptr_ty,
+                            .orig_ty = manyptr_ty,
                             .val = new_decl_val,
                         } },
                     } }),
@@ -18031,12 +18032,13 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     .ty = array_errors_ty.toIntern(),
                     .storage = .{ .elems = vals },
                 } });
+                const manyptr_errors_ty = slice_errors_ty.slicePtrFieldType(mod).toIntern();
                 break :v try mod.intern(.{ .slice = .{
                     .ty = slice_errors_ty.toIntern(),
                     .ptr = try mod.intern(.{ .ptr = .{
-                        .ty = slice_errors_ty.slicePtrFieldType(mod).toIntern(),
+                        .ty = manyptr_errors_ty,
                         .addr = .{ .anon_decl = .{
-                            .orig_ty = slice_errors_ty.toIntern(),
+                            .orig_ty = manyptr_errors_ty,
                             .val = new_decl_val,
                         } },
                     } }),
@@ -18155,20 +18157,21 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     .ty = fields_array_ty.toIntern(),
                     .storage = .{ .elems = enum_field_vals },
                 } });
-                const ptr_ty = (try sema.ptrType(.{
+                const slice_ty = (try sema.ptrType(.{
                     .child = enum_field_ty.toIntern(),
                     .flags = .{
                         .size = .Slice,
                         .is_const = true,
                     },
                 })).toIntern();
+                const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(mod).toIntern();
                 break :v try mod.intern(.{ .slice = .{
-                    .ty = ptr_ty,
+                    .ty = slice_ty,
                     .ptr = try mod.intern(.{ .ptr = .{
-                        .ty = Type.fromInterned(ptr_ty).slicePtrFieldType(mod).toIntern(),
+                        .ty = manyptr_ty,
                         .addr = .{ .anon_decl = .{
                             .val = new_decl_val,
-                            .orig_ty = ptr_ty,
+                            .orig_ty = manyptr_ty,
                         } },
                     } }),
                     .len = (try mod.intValue(Type.usize, enum_field_vals.len)).toIntern(),
@@ -18296,19 +18299,20 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     .ty = array_fields_ty.toIntern(),
                     .storage = .{ .elems = union_field_vals },
                 } });
-                const ptr_ty = (try sema.ptrType(.{
+                const slice_ty = (try sema.ptrType(.{
                     .child = union_field_ty.toIntern(),
                     .flags = .{
                         .size = .Slice,
                         .is_const = true,
                     },
                 })).toIntern();
+                const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(mod).toIntern();
                 break :v try mod.intern(.{ .slice = .{
-                    .ty = ptr_ty,
+                    .ty = slice_ty,
                     .ptr = try mod.intern(.{ .ptr = .{
-                        .ty = Type.fromInterned(ptr_ty).slicePtrFieldType(mod).toIntern(),
+                        .ty = manyptr_ty,
                         .addr = .{ .anon_decl = .{
-                            .orig_ty = ptr_ty,
+                            .orig_ty = manyptr_ty,
                             .val = new_decl_val,
                         } },
                     } }),
@@ -18523,19 +18527,20 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     .ty = array_fields_ty.toIntern(),
                     .storage = .{ .elems = struct_field_vals },
                 } });
-                const ptr_ty = (try sema.ptrType(.{
+                const slice_ty = (try sema.ptrType(.{
                     .child = struct_field_ty.toIntern(),
                     .flags = .{
                         .size = .Slice,
                         .is_const = true,
                     },
                 })).toIntern();
+                const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(mod).toIntern();
                 break :v try mod.intern(.{ .slice = .{
-                    .ty = ptr_ty,
+                    .ty = slice_ty,
                     .ptr = try mod.intern(.{ .ptr = .{
-                        .ty = Type.fromInterned(ptr_ty).slicePtrFieldType(mod).toIntern(),
+                        .ty = manyptr_ty,
                         .addr = .{ .anon_decl = .{
-                            .orig_ty = ptr_ty,
+                            .orig_ty = manyptr_ty,
                             .val = new_decl_val,
                         } },
                     } }),
@@ -18661,19 +18666,20 @@ fn typeInfoDecls(
         .ty = array_decl_ty.toIntern(),
         .storage = .{ .elems = decl_vals.items },
     } });
-    const ptr_ty = (try sema.ptrType(.{
+    const slice_ty = (try sema.ptrType(.{
         .child = declaration_ty.toIntern(),
         .flags = .{
             .size = .Slice,
             .is_const = true,
         },
     })).toIntern();
+    const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(mod).toIntern();
     return try mod.intern(.{ .slice = .{
-        .ty = ptr_ty,
+        .ty = slice_ty,
         .ptr = try mod.intern(.{ .ptr = .{
-            .ty = Type.fromInterned(ptr_ty).slicePtrFieldType(mod).toIntern(),
+            .ty = manyptr_ty,
             .addr = .{ .anon_decl = .{
-                .orig_ty = ptr_ty,
+                .orig_ty = manyptr_ty,
                 .val = new_decl_val,
             } },
         } }),
@@ -19803,8 +19809,18 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
         break :blk @intCast(host_size);
     } else 0;
 
-    if (host_size != 0 and bit_offset >= host_size * 8) {
-        return sema.fail(block, bitoffset_src, "bit offset starts after end of host integer", .{});
+    if (host_size != 0) {
+        if (bit_offset >= host_size * 8) {
+            return sema.fail(block, bitoffset_src, "packed type '{}' at bit offset {} starts {} bits after the end of a {} byte host integer", .{
+                elem_ty.fmt(mod), bit_offset, bit_offset - host_size * 8, host_size,
+            });
+        }
+        const elem_bit_size = try elem_ty.bitSizeAdvanced(mod, sema);
+        if (elem_bit_size > host_size * 8 - bit_offset) {
+            return sema.fail(block, bitoffset_src, "packed type '{}' at bit offset {} ends {} bits after the end of a {} byte host integer", .{
+                elem_ty.fmt(mod), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size,
+            });
+        }
     }
 
     if (elem_ty.zigTypeTag(mod) == .Fn) {
@@ -24844,106 +24860,147 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 }
 
 fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
-    const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data;
-    const src = inst_data.src();
-    const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
-    const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
-    const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
-
-    const parent_ty = try sema.resolveType(block, ty_src, extra.parent_type);
-    const field_name = try sema.resolveConstStringIntern(block, name_src, extra.field_name, .{
-        .needed_comptime_reason = "field name must be comptime-known",
-    });
-    const field_ptr = try sema.resolveInst(extra.field_ptr);
-    const field_ptr_ty = sema.typeOf(field_ptr);
     const mod = sema.mod;
     const ip = &mod.intern_pool;
 
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
+    const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data;
+    const inst_src = inst_data.src();
+    const parent_ptr_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+    const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+    const field_ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
+
+    const parent_ptr_ty = try sema.resolveType(block, parent_ptr_ty_src, extra.parent_type);
+    try sema.checkPtrType(block, parent_ptr_ty_src, parent_ptr_ty, false);
+    if (!parent_ptr_ty.isSinglePointer(mod)) {
+        return sema.fail(block, parent_ptr_ty_src, "expected single pointer type, found '{}'", .{parent_ptr_ty.fmt(sema.mod)});
+    }
+    const parent_ty = parent_ptr_ty.childType(mod);
     if (parent_ty.zigTypeTag(mod) != .Struct and parent_ty.zigTypeTag(mod) != .Union) {
-        return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)});
+        return sema.fail(block, parent_ptr_ty_src, "expected pointer to struct or union type, found '{}'", .{parent_ptr_ty.fmt(sema.mod)});
     }
     try sema.resolveTypeLayout(parent_ty);
 
+    const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{
+        .needed_comptime_reason = "field name must be comptime-known",
+    });
     const field_index = switch (parent_ty.zigTypeTag(mod)) {
         .Struct => blk: {
             if (parent_ty.isTuple(mod)) {
                 if (ip.stringEqlSlice(field_name, "len")) {
-                    return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
+                    return sema.fail(block, inst_src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
                 }
-                break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, name_src);
+                break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, field_name_src);
             } else {
-                break :blk try sema.structFieldIndex(block, parent_ty, field_name, name_src);
+                break :blk try sema.structFieldIndex(block, parent_ty, field_name, field_name_src);
             }
         },
-        .Union => try sema.unionFieldIndex(block, parent_ty, field_name, name_src),
+        .Union => try sema.unionFieldIndex(block, parent_ty, field_name, field_name_src),
         else => unreachable,
     };
-
     if (parent_ty.zigTypeTag(mod) == .Struct and parent_ty.structFieldIsComptime(field_index, mod)) {
-        return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{});
+        return sema.fail(block, field_name_src, "cannot get @fieldParentPtr of a comptime field", .{});
     }
 
-    try sema.checkPtrOperand(block, ptr_src, field_ptr_ty);
-    const field_ptr_ty_info = field_ptr_ty.ptrInfo(mod);
+    const field_ptr = try sema.resolveInst(extra.field_ptr);
+    const field_ptr_ty = sema.typeOf(field_ptr);
+    try sema.checkPtrOperand(block, field_ptr_src, field_ptr_ty);
+    const field_ptr_info = field_ptr_ty.ptrInfo(mod);
 
-    var ptr_ty_data: InternPool.Key.PtrType = .{
-        .child = parent_ty.structFieldType(field_index, mod).toIntern(),
+    var actual_parent_ptr_info: InternPool.Key.PtrType = .{
+        .child = parent_ty.toIntern(),
         .flags = .{
-            .address_space = field_ptr_ty_info.flags.address_space,
-            .is_const = field_ptr_ty_info.flags.is_const,
+            .alignment = try parent_ptr_ty.ptrAlignmentAdvanced(mod, sema),
+            .is_const = field_ptr_info.flags.is_const,
+            .is_volatile = field_ptr_info.flags.is_volatile,
+            .is_allowzero = field_ptr_info.flags.is_allowzero,
+            .address_space = field_ptr_info.flags.address_space,
         },
+        .packed_offset = parent_ptr_ty.ptrInfo(mod).packed_offset,
     };
+    const field_ty = parent_ty.structFieldType(field_index, mod);
+    var actual_field_ptr_info: InternPool.Key.PtrType = .{
+        .child = field_ty.toIntern(),
+        .flags = .{
+            .alignment = try field_ptr_ty.ptrAlignmentAdvanced(mod, sema),
+            .is_const = field_ptr_info.flags.is_const,
+            .is_volatile = field_ptr_info.flags.is_volatile,
+            .is_allowzero = field_ptr_info.flags.is_allowzero,
+            .address_space = field_ptr_info.flags.address_space,
+        },
+        .packed_offset = field_ptr_info.packed_offset,
+    };
+    switch (parent_ty.containerLayout(mod)) {
+        .auto => {
+            actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(
+                if (mod.typeToStruct(parent_ty)) |struct_obj| try sema.structFieldAlignment(
+                    struct_obj.fieldAlign(ip, field_index),
+                    field_ty,
+                    struct_obj.layout,
+                ) else if (mod.typeToUnion(parent_ty)) |union_obj|
+                    try sema.unionFieldAlignment(union_obj, field_index)
+                else
+                    actual_field_ptr_info.flags.alignment,
+            );
 
-    if (parent_ty.containerLayout(mod) == .@"packed") {
-        return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{});
-    } else {
-        ptr_ty_data.flags.alignment = blk: {
-            if (mod.typeToStruct(parent_ty)) |struct_type| {
-                break :blk struct_type.fieldAlign(ip, field_index);
-            } else if (mod.typeToUnion(parent_ty)) |union_obj| {
-                break :blk union_obj.fieldAlign(ip, field_index);
-            } else {
-                break :blk .none;
-            }
-        };
-    }
-
-    const actual_field_ptr_ty = try sema.ptrType(ptr_ty_data);
-    const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src);
+            actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
+            actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
+        },
+        .@"extern" => {
+            const field_offset = parent_ty.structFieldOffset(field_index, mod);
+            actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (field_offset > 0)
+                Alignment.fromLog2Units(@ctz(field_offset))
+            else
+                actual_field_ptr_info.flags.alignment);
 
-    ptr_ty_data.child = parent_ty.toIntern();
-    const result_ptr = try sema.ptrType(ptr_ty_data);
+            actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
+            actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
+        },
+        .@"packed" => {
+            const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) +
+                (if (mod.typeToStruct(parent_ty)) |struct_obj| mod.structPackedFieldBitOffset(struct_obj, field_index) else 0) -
+                actual_field_ptr_info.packed_offset.bit_offset), 8) catch
+                return sema.fail(block, inst_src, "pointer bit-offset mismatch", .{});
+            actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (byte_offset > 0)
+                Alignment.fromLog2Units(@ctz(byte_offset))
+            else
+                actual_field_ptr_info.flags.alignment);
+        },
+    }
 
-    if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| {
+    const actual_field_ptr_ty = try sema.ptrType(actual_field_ptr_info);
+    const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, field_ptr_src);
+    const actual_parent_ptr_ty = try sema.ptrType(actual_parent_ptr_info);
+    const result = if (try sema.resolveDefinedValue(block, field_ptr_src, casted_field_ptr)) |field_ptr_val| result: {
         const field = switch (ip.indexToKey(field_ptr_val.toIntern())) {
             .ptr => |ptr| switch (ptr.addr) {
                 .field => |field| field,
                 else => null,
             },
             else => null,
-        } orelse return sema.fail(block, ptr_src, "pointer value not based on parent struct", .{});
+        } orelse return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
 
         if (field.index != field_index) {
-            return sema.fail(block, src, "field '{}' has index '{d}' but pointer value is index '{d}' of struct '{}'", .{
+            return sema.fail(block, inst_src, "field '{}' has index '{d}' but pointer value is index '{d}' of struct '{}'", .{
                 field_name.fmt(ip), field_index, field.index, parent_ty.fmt(sema.mod),
             });
         }
-        return Air.internedToRef(field.base);
-    }
-
-    try sema.requireRuntimeBlock(block, src, ptr_src);
-    try sema.queueFullTypeResolution(result_ptr);
-    return block.addInst(.{
-        .tag = .field_parent_ptr,
-        .data = .{ .ty_pl = .{
-            .ty = Air.internedToRef(result_ptr.toIntern()),
-            .payload = try block.sema.addExtra(Air.FieldParentPtr{
-                .field_ptr = casted_field_ptr,
-                .field_index = @intCast(field_index),
-            }),
-        } },
-    });
+        break :result try sema.coerce(block, actual_parent_ptr_ty, Air.internedToRef(field.base), inst_src);
+    } else result: {
+        try sema.requireRuntimeBlock(block, inst_src, field_ptr_src);
+        try sema.queueFullTypeResolution(parent_ty);
+        break :result try block.addInst(.{
+            .tag = .field_parent_ptr,
+            .data = .{ .ty_pl = .{
+                .ty = Air.internedToRef(actual_parent_ptr_ty.toIntern()),
+                .payload = try block.sema.addExtra(Air.FieldParentPtr{
+                    .field_ptr = casted_field_ptr,
+                    .field_index = @intCast(field_index),
+                }),
+            } },
+        });
+    };
+    return sema.coerce(block, parent_ptr_ty, result, inst_src);
 }
 
 fn zirMinMax(
test/behavior/align.zig
@@ -693,5 +693,5 @@ test "zero-bit fields in extern struct pad fields appropriately" {
     try expect(@intFromPtr(&s) % 2 == 0);
     try expect(@intFromPtr(&s.y) - @intFromPtr(&s.x) == 2);
     try expect(@intFromPtr(&s.y) == @intFromPtr(&s.a));
-    try expect(@fieldParentPtr(S, "a", &s.a) == &s);
+    try expect(@fieldParentPtr(*S, "a", &s.a) == &s);
 }
test/behavior/field_parent_ptr.zig
@@ -1,126 +1,1924 @@
 const expect = @import("std").testing.expect;
 const builtin = @import("builtin");
 
-test "@fieldParentPtr non-first field" {
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
+test "@fieldParentPtr struct" {
+    const C = struct {
+        a: bool = true,
+        b: f32 = 3.14,
+        c: struct { u8 } = .{42},
+        d: i32 = 12345,
+    };
 
-    try testParentFieldPtr(&foo.c);
-    try comptime testParentFieldPtr(&foo.c);
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .b = 666.667 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 666.667 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 666.667 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 666.667 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .c = .{255} };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{255} };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{255} };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = .{255} };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "d", pcf));
+        try expect(pc == &c);
+    }
 }
 
-test "@fieldParentPtr first field" {
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
+test "@fieldParentPtr extern struct" {
+    const C = extern struct {
+        a: bool = true,
+        b: f32 = 3.14,
+        c: extern struct { x: u8 } = .{ .x = 42 },
+        d: i32 = 12345,
+    };
+
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .b = 666.667 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 666.667 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 666.667 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 666.667 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
 
-    try testParentFieldPtrFirst(&foo.a);
-    try comptime testParentFieldPtrFirst(&foo.a);
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = .{ .x = 255 } };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "d", pcf));
+        try expect(pc == &c);
+    }
 }
 
-const Foo = struct {
-    a: bool,
-    b: f32,
-    c: i32,
-    d: i32,
-};
+test "@fieldParentPtr extern struct first zero-bit field" {
+    const C = extern struct {
+        a: u0 = 0,
+        b: f32 = 3.14,
+        c: i32 = 12345,
+    };
 
-const foo = Foo{
-    .a = true,
-    .b = 0.123,
-    .c = 1234,
-    .d = -10,
-};
+    {
+        const c: C = .{ .a = 0 };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 0 };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 0 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = 0 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
 
-fn testParentFieldPtr(c: *const i32) !void {
-    try expect(c == &foo.c);
+    {
+        const c: C = .{ .b = 666.667 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 666.667 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 666.667 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 666.667 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
 
-    const base = @fieldParentPtr(Foo, "c", c);
-    try expect(base == &foo);
-    try expect(&base.c == c);
+    {
+        const c: C = .{ .c = -1111111111 };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = -1111111111 };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = -1111111111 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = -1111111111 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
 }
 
-fn testParentFieldPtrFirst(a: *const bool) !void {
-    try expect(a == &foo.a);
+test "@fieldParentPtr extern struct middle zero-bit field" {
+    const C = extern struct {
+        a: f32 = 3.14,
+        b: u0 = 0,
+        c: i32 = 12345,
+    };
+
+    {
+        const c: C = .{ .a = 666.667 };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 666.667 };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 666.667 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = 666.667 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
 
-    const base = @fieldParentPtr(Foo, "a", a);
-    try expect(base == &foo);
-    try expect(&base.a == a);
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .c = -1111111111 };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = -1111111111 };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = -1111111111 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = -1111111111 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
 }
 
-test "@fieldParentPtr untagged union" {
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+test "@fieldParentPtr extern struct last zero-bit field" {
+    const C = extern struct {
+        a: f32 = 3.14,
+        b: i32 = 12345,
+        c: u0 = 0,
+    };
+
+    {
+        const c: C = .{ .a = 666.667 };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 666.667 };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 666.667 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = 666.667 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .b = -1111111111 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = -1111111111 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = -1111111111 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = -1111111111 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .c = 0 };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = 0 };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = 0 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = 0 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
+}
+
+test "@fieldParentPtr unaligned packed struct" {
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+
+    const C = packed struct {
+        a: bool = true,
+        b: f32 = 3.14,
+        c: packed struct { x: u8 } = .{ .x = 42 },
+        d: i32 = 12345,
+    };
+
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .b = 666.667 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 666.667 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 666.667 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 666.667 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
 
-    try testFieldParentPtrUnion(&bar.c);
-    try comptime testFieldParentPtrUnion(&bar.c);
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = .{ .x = 255 } };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "d", pcf));
+        try expect(pc == &c);
+    }
 }
 
-const Bar = union(enum) {
-    a: bool,
-    b: f32,
-    c: i32,
-    d: i32,
-};
+test "@fieldParentPtr aligned packed struct" {
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+
+    const C = packed struct {
+        a: f32 = 3.14,
+        b: i32 = 12345,
+        c: packed struct { x: u8 } = .{ .x = 42 },
+        d: bool = true,
+    };
+
+    {
+        const c: C = .{ .a = 666.667 };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 666.667 };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 666.667 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = 666.667 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .b = -1111111111 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = -1111111111 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = -1111111111 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = -1111111111 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = .{ .x = 255 } };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .d = false };
+        const pcf = &c.d;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = false };
+        const pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = false };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .d = false };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "d", pcf));
+        try expect(pc == &c);
+    }
+}
+
+test "@fieldParentPtr nested packed struct" {
+    if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+
+    {
+        const C = packed struct {
+            a: u8,
+            b: packed struct {
+                a: u8,
+                b: packed struct {
+                    a: u8,
+                },
+            },
+        };
+
+        {
+            const c: C = .{ .a = 0, .b = .{ .a = 0, .b = .{ .a = 0 } } };
+            const pcbba = &c.b.b.a;
+            const pcbb: @TypeOf(&c.b.b) = @alignCast(@fieldParentPtr(*align(1) const @TypeOf(c.b.b), "a", pcbba));
+            try expect(pcbb == &c.b.b);
+            const pcb: @TypeOf(&c.b) = @alignCast(@fieldParentPtr(*align(1) const @TypeOf(c.b), "b", pcbb));
+            try expect(pcb == &c.b);
+            const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcb));
+            try expect(pc == &c);
+        }
+
+        {
+            var c: C = undefined;
+            c = .{ .a = 0, .b = .{ .a = 0, .b = .{ .a = 0 } } };
+            var pcbba: @TypeOf(&c.b.b.a) = undefined;
+            pcbba = &c.b.b.a;
+            var pcbb: @TypeOf(&c.b.b) = undefined;
+            pcbb = @alignCast(@fieldParentPtr(*align(1) @TypeOf(c.b.b), "a", pcbba));
+            try expect(pcbb == &c.b.b);
+            var pcb: @TypeOf(&c.b) = undefined;
+            pcb = @alignCast(@fieldParentPtr(*align(1) @TypeOf(c.b), "b", pcbb));
+            try expect(pcb == &c.b);
+            var pc: *C = undefined;
+            pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcb));
+            try expect(pc == &c);
+        }
+    }
+
+    {
+        const C = packed struct {
+            a: u8,
+            b: packed struct {
+                a: u9,
+                b: packed struct {
+                    a: u8,
+                },
+            },
+        };
+
+        {
+            const c: C = .{ .a = 0, .b = .{ .a = 0, .b = .{ .a = 0 } } };
+            const pcbba = &c.b.b.a;
+            const pcbb: @TypeOf(&c.b.b) = @alignCast(@fieldParentPtr(*align(1:17:4) const @TypeOf(c.b.b), "a", pcbba));
+            try expect(pcbb == &c.b.b);
+            const pcb: @TypeOf(&c.b) = @alignCast(@fieldParentPtr(*align(1:8:4) const @TypeOf(c.b), "b", pcbb));
+            try expect(pcb == &c.b);
+            const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcb));
+            try expect(pc == &c);
+        }
+
+        {
+            var c: C = undefined;
+            c = .{ .a = 0, .b = .{ .a = 0, .b = .{ .a = 0 } } };
+            var pcbba: @TypeOf(&c.b.b.a) = undefined;
+            pcbba = &c.b.b.a;
+            var pcbb: @TypeOf(&c.b.b) = undefined;
+            pcbb = @alignCast(@fieldParentPtr(*align(1:17:4) @TypeOf(c.b.b), "a", pcbba));
+            try expect(pcbb == &c.b.b);
+            var pcb: @TypeOf(&c.b) = undefined;
+            pcb = @alignCast(@fieldParentPtr(*align(1:8:4) @TypeOf(c.b), "b", pcbb));
+            try expect(pcb == &c.b);
+            var pc: *C = undefined;
+            pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcb));
+            try expect(pc == &c);
+        }
+    }
+
+    {
+        const C = packed struct {
+            a: u9,
+            b: packed struct {
+                a: u7,
+                b: packed struct {
+                    a: u8,
+                },
+            },
+        };
+
+        {
+            const c: C = .{ .a = 0, .b = .{ .a = 0, .b = .{ .a = 0 } } };
+            const pcbba = &c.b.b.a;
+            const pcbb: @TypeOf(&c.b.b) = @alignCast(@fieldParentPtr(*align(1) const @TypeOf(c.b.b), "a", pcbba));
+            try expect(pcbb == &c.b.b);
+            const pcb: @TypeOf(&c.b) = @alignCast(@fieldParentPtr(*align(1:9:3) const @TypeOf(c.b), "b", pcbb));
+            try expect(pcb == &c.b);
+            const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcb));
+            try expect(pc == &c);
+        }
+
+        {
+            var c: C = undefined;
+            c = .{ .a = 0, .b = .{ .a = 0, .b = .{ .a = 0 } } };
+            var pcbba: @TypeOf(&c.b.b.a) = undefined;
+            pcbba = &c.b.b.a;
+            var pcbb: @TypeOf(&c.b.b) = undefined;
+            pcbb = @alignCast(@fieldParentPtr(*align(1) @TypeOf(c.b.b), "a", pcbba));
+            try expect(pcbb == &c.b.b);
+            var pcb: @TypeOf(&c.b) = undefined;
+            pcb = @alignCast(@fieldParentPtr(*align(1:9:3) @TypeOf(c.b), "b", pcbb));
+            try expect(pcb == &c.b);
+            var pc: *C = undefined;
+            pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcb));
+            try expect(pc == &c);
+        }
+    }
 
-const bar = Bar{ .c = 42 };
+    {
+        const C = packed struct {
+            a: u9,
+            b: packed struct {
+                a: u8,
+                b: packed struct {
+                    a: u8,
+                },
+            },
+        };
 
-fn testFieldParentPtrUnion(c: *const i32) !void {
-    try expect(c == &bar.c);
+        {
+            const c: C = .{ .a = 0, .b = .{ .a = 0, .b = .{ .a = 0 } } };
+            const pcbba = &c.b.b.a;
+            const pcbb: @TypeOf(&c.b.b) = @alignCast(@fieldParentPtr(*align(1:17:4) const @TypeOf(c.b.b), "a", pcbba));
+            try expect(pcbb == &c.b.b);
+            const pcb: @TypeOf(&c.b) = @alignCast(@fieldParentPtr(*align(1:9:4) const @TypeOf(c.b), "b", pcbb));
+            try expect(pcb == &c.b);
+            const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcb));
+            try expect(pc == &c);
+        }
 
-    const base = @fieldParentPtr(Bar, "c", c);
-    try expect(base == &bar);
-    try expect(&base.c == c);
+        {
+            var c: C = undefined;
+            c = .{ .a = 0, .b = .{ .a = 0, .b = .{ .a = 0 } } };
+            var pcbba: @TypeOf(&c.b.b.a) = undefined;
+            pcbba = &c.b.b.a;
+            var pcbb: @TypeOf(&c.b.b) = undefined;
+            pcbb = @alignCast(@fieldParentPtr(*align(1:17:4) @TypeOf(c.b.b), "a", pcbba));
+            try expect(pcbb == &c.b.b);
+            var pcb: @TypeOf(&c.b) = undefined;
+            pcb = @alignCast(@fieldParentPtr(*align(1:9:4) @TypeOf(c.b), "b", pcbb));
+            try expect(pcb == &c.b);
+            var pc: *C = undefined;
+            pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcb));
+            try expect(pc == &c);
+        }
+    }
+}
+
+test "@fieldParentPtr packed struct first zero-bit field" {
+    if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+
+    const C = packed struct {
+        a: u0 = 0,
+        b: f32 = 3.14,
+        c: i32 = 12345,
+    };
+
+    {
+        const c: C = .{ .a = 0 };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 0 };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 0 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = 0 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .b = 666.667 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 666.667 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 666.667 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 666.667 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .c = -1111111111 };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = -1111111111 };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = -1111111111 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = -1111111111 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
+}
+
+test "@fieldParentPtr packed struct middle zero-bit field" {
+    if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+
+    const C = packed struct {
+        a: f32 = 3.14,
+        b: u0 = 0,
+        c: i32 = 12345,
+    };
+
+    {
+        const c: C = .{ .a = 666.667 };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 666.667 };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 666.667 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = 666.667 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .c = -1111111111 };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = -1111111111 };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = -1111111111 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = -1111111111 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
+}
+
+test "@fieldParentPtr packed struct last zero-bit field" {
+    if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+
+    const C = packed struct {
+        a: f32 = 3.14,
+        b: i32 = 12345,
+        c: u0 = 0,
+    };
+
+    {
+        const c: C = .{ .a = 666.667 };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 666.667 };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 666.667 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = 666.667 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .b = -1111111111 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = -1111111111 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = -1111111111 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = -1111111111 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .c = 0 };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = 0 };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = 0 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = 0 };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
 }
 
 test "@fieldParentPtr tagged union" {
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    const C = union(enum) {
+        a: bool,
+        b: f32,
+        c: struct { u8 },
+        d: i32,
+    };
+
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .c = .{255} };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{255} };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{255} };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = .{255} };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
 
-    try testFieldParentPtrTaggedUnion(&bar_tagged.c);
-    try comptime testFieldParentPtrTaggedUnion(&bar_tagged.c);
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "d", pcf));
+        try expect(pc == &c);
+    }
 }
 
-const BarTagged = union(enum) {
-    a: bool,
-    b: f32,
-    c: i32,
-    d: i32,
-};
+test "@fieldParentPtr untagged union" {
+    const C = union {
+        a: bool,
+        b: f32,
+        c: struct { u8 },
+        d: i32,
+    };
+
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
 
-const bar_tagged = BarTagged{ .c = 42 };
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
 
-fn testFieldParentPtrTaggedUnion(c: *const i32) !void {
-    try expect(c == &bar_tagged.c);
+    {
+        const c: C = .{ .c = .{255} };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{255} };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{255} };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = .{255} };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
 
-    const base = @fieldParentPtr(BarTagged, "c", c);
-    try expect(base == &bar_tagged);
-    try expect(&base.c == c);
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "d", pcf));
+        try expect(pc == &c);
+    }
 }
 
 test "@fieldParentPtr extern union" {
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    const C = extern union {
+        a: bool,
+        b: f32,
+        c: extern struct { x: u8 },
+        d: i32,
+    };
+
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = .{ .x = 255 } };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "d", pcf));
+        try expect(pc == &c);
+    }
+}
+
+test "@fieldParentPtr packed union" {
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+
+    const C = packed union {
+        a: bool,
+        b: f32,
+        c: packed struct { x: u8 },
+        d: i32,
+    };
+
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = false };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
+
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        const pcf = &c.c;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        const pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .c = .{ .x = 255 } };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "c", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .c = .{ .x = 255 } };
+        var pcf: @TypeOf(&c.c) = undefined;
+        pcf = &c.c;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "c", pcf));
+        try expect(pc == &c);
+    }
 
-    try testFieldParentPtrExternUnion(&bar_extern.c);
-    try comptime testFieldParentPtrExternUnion(&bar_extern.c);
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        const pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "d", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .d = -1111111111 };
+        var pcf: @TypeOf(&c.d) = undefined;
+        pcf = &c.d;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "d", pcf));
+        try expect(pc == &c);
+    }
 }
 
-const BarExtern = extern union {
-    a: bool,
-    b: f32,
-    c: i32,
-    d: i32,
-};
+test "@fieldParentPtr tagged union all zero-bit fields" {
+    if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
 
-const bar_extern = BarExtern{ .c = 42 };
+    const C = union(enum) {
+        a: u0,
+        b: i0,
+    };
 
-fn testFieldParentPtrExternUnion(c: *const i32) !void {
-    try expect(c == &bar_extern.c);
+    {
+        const c: C = .{ .a = 0 };
+        const pcf = &c.a;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 0 };
+        const pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .a = 0 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "a", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .a = 0 };
+        var pcf: @TypeOf(&c.a) = undefined;
+        pcf = &c.a;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "a", pcf));
+        try expect(pc == &c);
+    }
 
-    const base = @fieldParentPtr(BarExtern, "c", c);
-    try expect(base == &bar_extern);
-    try expect(&base.c == c);
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        const pc: *const C = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        const pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        const c: C = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *const C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) const C, "b", pcf));
+        try expect(pc == &c);
+    }
+    {
+        var c: C = undefined;
+        c = .{ .b = 0 };
+        var pcf: @TypeOf(&c.b) = undefined;
+        pcf = &c.b;
+        var pc: *C = undefined;
+        pc = @alignCast(@fieldParentPtr(*align(1) C, "b", pcf));
+        try expect(pc == &c);
+    }
 }
test/behavior/struct.zig
@@ -1392,13 +1392,13 @@ test "fieldParentPtr of a zero-bit field" {
             {
                 const a = A{ .u = 0 };
                 const b_ptr = &a.b;
-                const a_ptr = @fieldParentPtr(A, "b", b_ptr);
+                const a_ptr = @fieldParentPtr(*const A, "b", b_ptr);
                 try std.testing.expectEqual(&a, a_ptr);
             }
             {
                 var a = A{ .u = 0 };
                 const b_ptr = &a.b;
-                const a_ptr = @fieldParentPtr(A, "b", b_ptr);
+                const a_ptr = @fieldParentPtr(*const A, "b", b_ptr);
                 try std.testing.expectEqual(&a, a_ptr);
             }
         }
@@ -1406,17 +1406,17 @@ test "fieldParentPtr of a zero-bit field" {
             {
                 const a = A{ .u = 0 };
                 const c_ptr = &a.b.c;
-                const b_ptr = @fieldParentPtr(@TypeOf(a.b), "c", c_ptr);
+                const b_ptr = @fieldParentPtr(*const @TypeOf(a.b), "c", c_ptr);
                 try std.testing.expectEqual(&a.b, b_ptr);
-                const a_ptr = @fieldParentPtr(A, "b", b_ptr);
+                const a_ptr = @fieldParentPtr(*const A, "b", b_ptr);
                 try std.testing.expectEqual(&a, a_ptr);
             }
             {
                 var a = A{ .u = 0 };
                 const c_ptr = &a.b.c;
-                const b_ptr = @fieldParentPtr(@TypeOf(a.b), "c", c_ptr);
+                const b_ptr = @fieldParentPtr(*const @TypeOf(a.b), "c", c_ptr);
                 try std.testing.expectEqual(&a.b, b_ptr);
-                const a_ptr = @fieldParentPtr(A, "b", b_ptr);
+                const a_ptr = @fieldParentPtr(*const A, "b", b_ptr);
                 try std.testing.expectEqual(&a, a_ptr);
             }
         }
test/behavior/tuple.zig
@@ -222,7 +222,7 @@ test "fieldParentPtr of tuple" {
     var x: u32 = 0;
     _ = &x;
     const tuple = .{ x, x };
-    try testing.expect(&tuple == @fieldParentPtr(@TypeOf(tuple), "1", &tuple[1]));
+    try testing.expect(&tuple == @fieldParentPtr(*const @TypeOf(tuple), "1", &tuple[1]));
 }
 
 test "fieldParentPtr of anon struct" {
@@ -233,7 +233,7 @@ test "fieldParentPtr of anon struct" {
     var x: u32 = 0;
     _ = &x;
     const anon_st = .{ .foo = x, .bar = x };
-    try testing.expect(&anon_st == @fieldParentPtr(@TypeOf(anon_st), "bar", &anon_st.bar));
+    try testing.expect(&anon_st == @fieldParentPtr(*const @TypeOf(anon_st), "bar", &anon_st.bar));
 }
 
 test "offsetOf tuple" {
test/cases/compile_errors/fieldParentPtr-bad_field_name.zig
@@ -2,12 +2,12 @@ const Foo = extern struct {
     derp: i32,
 };
 export fn foo(a: *i32) *Foo {
-    return @fieldParentPtr(Foo, "a", a);
+    return @fieldParentPtr(*Foo, "a", a);
 }
 
 // error
 // backend=stage2
 // target=native
 //
-// :5:33: error: no field named 'a' in struct 'tmp.Foo'
+// :5:34: error: no field named 'a' in struct 'tmp.Foo'
 // :1:20: note: struct declared here
test/cases/compile_errors/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig
@@ -9,7 +9,7 @@ const foo = Foo{
 
 comptime {
     const field_ptr: *i32 = @ptrFromInt(0x1234);
-    const another_foo_ptr = @fieldParentPtr(Foo, "b", field_ptr);
+    const another_foo_ptr = @fieldParentPtr(*const Foo, "b", field_ptr);
     _ = another_foo_ptr;
 }
 
@@ -17,4 +17,4 @@ comptime {
 // backend=stage2
 // target=native
 //
-// :12:55: error: pointer value not based on parent struct
+// :12:62: error: pointer value not based on parent struct
test/cases/compile_errors/fieldParentPtr-comptime_wrong_field_index.zig
@@ -8,7 +8,7 @@ const foo = Foo{
 };
 
 comptime {
-    const another_foo_ptr = @fieldParentPtr(Foo, "b", &foo.a);
+    const another_foo_ptr = @fieldParentPtr(*const Foo, "b", &foo.a);
     _ = another_foo_ptr;
 }
 
test/cases/compile_errors/fieldParentPtr-field_pointer_is_not_pointer.zig
@@ -2,11 +2,11 @@ const Foo = extern struct {
     a: i32,
 };
 export fn foo(a: i32) *Foo {
-    return @fieldParentPtr(Foo, "a", a);
+    return @fieldParentPtr(*const Foo, "a", a);
 }
 
 // error
 // backend=stage2
 // target=native
 //
-// :5:38: error: expected pointer type, found 'i32'
+// :5:45: error: expected pointer type, found 'i32'
test/cases/compile_errors/fieldParentPtr-non_struct.zig → test/cases/compile_errors/fieldParentPtr-non_pointer.zig
@@ -7,4 +7,4 @@ export fn foo(a: *i32) *Foo {
 // backend=llvm
 // target=native
 //
-// :3:28: error: expected struct or union type, found 'i32'
+// :3:28: error: expected pointer type, found 'i32'
test/cases/compile_errors/fieldParentPtr_on_comptime_field.zig
@@ -5,7 +5,7 @@ pub export fn entry1() void {
     @offsetOf(T, "a");
 }
 pub export fn entry2() void {
-    @fieldParentPtr(T, "a", undefined);
+    @fieldParentPtr(*T, "a", undefined);
 }
 
 // error
@@ -13,4 +13,4 @@ pub export fn entry2() void {
 // target=native
 //
 // :5:5: error: no offset available for comptime field
-// :8:5: error: cannot get @fieldParentPtr of a comptime field
+// :8:25: error: cannot get @fieldParentPtr of a comptime field
test/cases/compile_errors/invalid_bit_pointer.zig
@@ -0,0 +1,13 @@
+comptime {
+    _ = *align(1:32:4) u8;
+}
+comptime {
+    _ = *align(1:25:4) u8;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:18: error: packed type 'u8' at bit offset 32 starts 0 bits after the end of a 4 byte host integer
+// :5:18: error: packed type 'u8' at bit offset 25 ends 1 bits after the end of a 4 byte host integer