atomics A.0c: harden guards (scalar-kind, ordering validity, align bail)
Adversarial review of A.0 found two silent-wrong defects reachable via the public atomic_load/atomic_store intrinsics (raw LLVM verifier errors, not clean sx diagnostics) + a latent alignment fallback. All fixed: - scalar-kind allowlist (call.zig): the size-only T guard admitted same-sized aggregates ([8]u8, 8-byte structs) -> invalid 'load atomic [8 x i8]'. Now an allowlist switch (integer/float/bool/pointer/enum/vector) rejects loudly. - per-op ordering validity (call.zig): load cannot release/acq_rel, store cannot acquire/acq_rel -> loud diagnostic instead of invalid LLVM. - val_ty align fallback (ops.zig): the 'else .i64' (align 8) default would over-align a sub-8 store -> now bails loudly on a missing val_ty. Locked by examples 1130 (non-scalar) + 1131 (bad ordering). Suite green (713/0).
This commit is contained in:
@@ -1705,9 +1705,18 @@ pub fn tryLowerAtomicIntrinsic(self: *Lowering, name: []const u8, c: *const ast.
|
||||
}
|
||||
|
||||
const elem_ty = self.resolveTypeArg(c.args[0]);
|
||||
// Atomic-eligible T = a SCALAR that LLVM can load/store atomically: integer,
|
||||
// float, bool, pointer, enum (integer-backed), or vector. Aggregates
|
||||
// (struct/array/slice/string/tuple/…) are rejected LOUDLY here — without the
|
||||
// kind check a same-sized aggregate (`[8]u8`, an 8-byte struct) slips through
|
||||
// and the user gets a raw LLVM verifier error instead of a clean diagnostic.
|
||||
const scalar_ok = switch (self.module.types.get(elem_ty)) {
|
||||
.signed, .unsigned, .f32, .f64, .bool, .pointer, .many_pointer, .cstring, .@"enum", .vector => true,
|
||||
else => false,
|
||||
};
|
||||
const size = self.typeSizeBytes(elem_ty);
|
||||
if (size != 1 and size != 2 and size != 4 and size != 8 and size != 16) {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, c.args[0].span, "atomic ops require a scalar type of size 1/2/4/8/16 bytes — '{s}' is {d} bytes", .{ self.formatTypeName(elem_ty), size });
|
||||
if (!scalar_ok or (size != 1 and size != 2 and size != 4 and size != 8 and size != 16)) {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, c.args[0].span, "atomic ops require a scalar type (integer/float/bool/pointer/enum/vector) of size 1/2/4/8/16 bytes — '{s}' is not eligible", .{self.formatTypeName(elem_ty)});
|
||||
return Ref.none;
|
||||
}
|
||||
|
||||
@@ -1716,6 +1725,16 @@ pub fn tryLowerAtomicIntrinsic(self: *Lowering, name: []const u8, c: *const ast.
|
||||
if (self.diagnostics) |d| d.addFmt(.err, ord_node.span, "atomic ordering must be a constant ordering literal (.relaxed / .acquire / .release / .acq_rel / .seq_cst)", .{});
|
||||
return Ref.none;
|
||||
};
|
||||
// Per-op ordering validity (LLVM rejects these). A load can't release; a
|
||||
// store can't acquire; neither can acq_rel. Loud diagnostic, not invalid IR.
|
||||
if (is_load and (ordering == .release or ordering == .acq_rel)) {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, ord_node.span, "atomic load ordering cannot be .release or .acq_rel (use .relaxed / .acquire / .seq_cst)", .{});
|
||||
return Ref.none;
|
||||
}
|
||||
if (is_store and (ordering == .acquire or ordering == .acq_rel)) {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, ord_node.span, "atomic store ordering cannot be .acquire or .acq_rel (use .relaxed / .release / .seq_cst)", .{});
|
||||
return Ref.none;
|
||||
}
|
||||
|
||||
const ptr = self.lowerExpr(c.args[1]);
|
||||
if (is_load) {
|
||||
|
||||
Reference in New Issue
Block a user