Commit 3ad81c40c0

Jacob Young <jacobly0@users.noreply.github.com>
2024-07-12 03:36:34
Zcu: allow atomic operations on packed structs
Same validation rules as the backing integer would have.
1 parent ca752c6
Changed files (5)
src/Sema.zig
@@ -23950,7 +23950,7 @@ fn checkAtomicPtrOperand(
         error.BadType => return sema.fail(
             block,
             elem_ty_src,
-            "expected bool, integer, float, enum, or pointer type; found '{}'",
+            "expected bool, integer, float, enum, packed struct, or pointer type; found '{}'",
             .{elem_ty.fmt(pt)},
         ),
     };
@@ -24279,7 +24279,7 @@ fn zirCmpxchg(
         return sema.fail(
             block,
             elem_ty_src,
-            "expected bool, integer, enum, or pointer type; found '{}'",
+            "expected bool, integer, enum, packed struct, or pointer type; found '{}'",
             .{elem_ty.fmt(pt)},
         );
     }
src/Zcu.zig
@@ -3305,37 +3305,31 @@ pub fn atomicPtrAlignment(
         .spirv => @panic("TODO what should this value be?"),
     };
 
-    const int_ty = switch (ty.zigTypeTag(mod)) {
-        .Int => ty,
-        .Enum => ty.intTagType(mod),
-        .Float => {
-            const bit_count = ty.floatBits(target);
-            if (bit_count > max_atomic_bits) {
-                diags.* = .{
-                    .bits = bit_count,
-                    .max_bits = max_atomic_bits,
-                };
-                return error.FloatTooBig;
-            }
-            return .none;
-        },
-        .Bool => return .none,
-        else => {
-            if (ty.isPtrAtRuntime(mod)) return .none;
-            return error.BadType;
-        },
-    };
-
-    const bit_count = int_ty.intInfo(mod).bits;
-    if (bit_count > max_atomic_bits) {
-        diags.* = .{
-            .bits = bit_count,
-            .max_bits = max_atomic_bits,
-        };
-        return error.IntTooBig;
+    if (ty.toIntern() == .bool_type) return .none;
+    if (ty.isRuntimeFloat()) {
+        const bit_count = ty.floatBits(target);
+        if (bit_count > max_atomic_bits) {
+            diags.* = .{
+                .bits = bit_count,
+                .max_bits = max_atomic_bits,
+            };
+            return error.FloatTooBig;
+        }
+        return .none;
+    }
+    if (ty.isAbiInt(mod)) {
+        const bit_count = ty.intInfo(mod).bits;
+        if (bit_count > max_atomic_bits) {
+            diags.* = .{
+                .bits = bit_count,
+                .max_bits = max_atomic_bits,
+            };
+            return error.IntTooBig;
+        }
+        return .none;
     }
-
-    return .none;
+    if (ty.isPtrAtRuntime(mod)) return .none;
+    return error.BadType;
 }
 
 pub fn declFileScope(mod: *Module, decl_index: Decl.Index) *File {
test/behavior/atomics.zig
@@ -413,6 +413,14 @@ test "atomics with different types" {
 
     try testAtomicsWithType(u0, 0, 0);
     try testAtomicsWithType(i0, 0, 0);
+
+    try testAtomicsWithType(enum(u32) { x = 1234, y = 5678 }, .x, .y);
+
+    try testAtomicsWithPackedStruct(
+        packed struct { x: u7, y: u24, z: bool },
+        .{ .x = 1, .y = 2, .z = true },
+        .{ .x = 3, .y = 4, .z = false },
+    );
 }
 
 fn testAtomicsWithType(comptime T: type, a: T, b: T) !void {
@@ -426,6 +434,18 @@ fn testAtomicsWithType(comptime T: type, a: T, b: T) !void {
         try expect(@cmpxchgStrong(T, &x, b, a, .seq_cst, .seq_cst).? == a);
 }
 
+fn testAtomicsWithPackedStruct(comptime T: type, a: T, b: T) !void {
+    const BackingInt = @typeInfo(T).Struct.backing_integer.?;
+    var x: T = b;
+    @atomicStore(T, &x, a, .seq_cst);
+    try expect(@as(BackingInt, @bitCast(x)) == @as(BackingInt, @bitCast(a)));
+    try expect(@as(BackingInt, @bitCast(@atomicLoad(T, &x, .seq_cst))) == @as(BackingInt, @bitCast(a)));
+    try expect(@as(BackingInt, @bitCast(@atomicRmw(T, &x, .Xchg, b, .seq_cst))) == @as(BackingInt, @bitCast(a)));
+    try expect(@cmpxchgStrong(T, &x, b, a, .seq_cst, .seq_cst) == null);
+    if (@sizeOf(T) != 0)
+        try expect(@as(BackingInt, @bitCast(@cmpxchgStrong(T, &x, b, a, .seq_cst, .seq_cst).?)) == @as(BackingInt, @bitCast(a)));
+}
+
 test "return @atomicStore, using it as a void value" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/cases/compile_errors/atomics_with_invalid_type.zig
@@ -0,0 +1,18 @@
+export fn float() void {
+    var x: f32 = 0;
+    _ = @cmpxchgWeak(f32, &x, 1, 2, .seq_cst, .seq_cst);
+}
+
+const NormalStruct = struct { x: u32 };
+export fn normalStruct() void {
+    var x: NormalStruct = 0;
+    _ = @cmpxchgWeak(NormalStruct, &x, .{ .x = 1 }, .{ .x = 2 }, .seq_cst, .seq_cst);
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :3:22: error: expected bool, integer, enum, packed struct, or pointer type; found 'f32'
+// :8:27: error: expected type 'tmp.NormalStruct', found 'comptime_int'
+// :6:22: note: struct declared here
test/cases/compile_errors/cmpxchg_with_float.zig
@@ -1,10 +0,0 @@
-export fn entry() void {
-    var x: f32 = 0;
-    _ = @cmpxchgWeak(f32, &x, 1, 2, .seq_cst, .seq_cst);
-}
-
-// error
-// backend=stage2
-// target=native
-//
-// :3:22: error: expected bool, integer, enum, or pointer type; found 'f32'