Commit fb523c6283

kcbanner <kcbanner@gmail.com>
2023-10-24 07:44:44
sema: when guessing union alignment, save the result and check if the guess was correct
1 parent 4d044ee
Changed files (3)
src/InternPool.zig
@@ -690,13 +690,13 @@ pub const Key = union(enum) {
         /// The returned pointer expires with any addition to the `InternPool`.
         pub fn size(self: @This(), ip: *InternPool) *u32 {
             const size_field_index = std.meta.fieldIndex(Tag.TypeUnion, "size").?;
-            return @ptrCast(&ip.extra.items[self.extra_index + size_field_index]);
+            return &ip.extra.items[self.extra_index + size_field_index];
         }
 
         /// The returned pointer expires with any addition to the `InternPool`.
         pub fn padding(self: @This(), ip: *InternPool) *u32 {
             const padding_field_index = std.meta.fieldIndex(Tag.TypeUnion, "padding").?;
-            return @ptrCast(&ip.extra.items[self.extra_index + padding_field_index]);
+            return &ip.extra.items[self.extra_index + padding_field_index];
         }
 
         pub fn haveFieldTypes(self: @This(), ip: *const InternPool) bool {
@@ -2965,9 +2965,9 @@ pub const Tag = enum(u8) {
     /// 1. field align: Alignment for each field; declaration order
     pub const TypeUnion = struct {
         flags: Flags,
-        // Only valid after .have_layout
+        /// Only valid after .have_layout
         size: u32,
-        // Only valid after .have_layout
+        /// Only valid after .have_layout
         padding: u32,
         decl: Module.Decl.Index,
         namespace: Module.Namespace.Index,
@@ -2983,8 +2983,9 @@ pub const Tag = enum(u8) {
             status: UnionType.Status,
             requires_comptime: RequiresComptime,
             assumed_runtime_bits: bool,
+            assumed_pointer_aligned: bool,
             alignment: Alignment,
-            _: u15 = 0,
+            _: u14 = 0,
         };
     };
 
src/Sema.zig
@@ -3200,6 +3200,7 @@ fn zirUnionDecl(
                 .any_aligned_fields = small.any_aligned_fields,
                 .requires_comptime = .unknown,
                 .assumed_runtime_bits = false,
+                .assumed_pointer_aligned = false,
                 .alignment = .none,
             },
             .decl = new_decl_index,
@@ -20989,6 +20990,7 @@ fn zirReify(
                     .any_aligned_fields = any_aligned_fields,
                     .requires_comptime = .unknown,
                     .assumed_runtime_bits = false,
+                    .assumed_pointer_aligned = false,
                     .alignment = .none,
                 },
                 .field_types = union_fields.items(.type),
@@ -34940,7 +34942,10 @@ pub fn resolveUnionAlignment(
         // We'll guess "pointer-aligned", if the union has an
         // underaligned pointer field then some allocations
         // might require explicit alignment.
-        return Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
+        union_type.flagsPtr(ip).assumed_pointer_aligned = true;
+        const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
+        union_type.flagsPtr(ip).alignment = result;
+        return result;
     }
 
     try sema.resolveTypeFieldsUnion(ty, union_type);
@@ -35064,6 +35069,18 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
         );
         return sema.failWithOwnedErrorMsg(null, msg);
     }
+
+    if (union_obj.flagsPtr(ip).assumed_pointer_aligned and
+        alignment.compareStrict(.neq, Alignment.fromByteUnits(@divExact(mod.getTarget().ptrBitWidth(), 8))))
+    {
+        const msg = try Module.ErrorMsg.create(
+            sema.gpa,
+            mod.declPtr(union_obj.decl).srcLoc(mod),
+            "union layout depends on being pointer aligned",
+            .{},
+        );
+        return sema.failWithOwnedErrorMsg(null, msg);
+    }
 }
 
 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to
test/behavior/union.zig
@@ -1868,3 +1868,16 @@ test "reinterpret packed union inside packed struct" {
     if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     try S.doTheTest();
 }
+
+test "union field is a pointer to an aligned version of itself" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+
+    const E = union {
+        next: *align(1) @This(),
+    };
+    var e: E = undefined;
+    e = .{ .next = &e };
+
+    try expect(&e == e.next);
+}