Commit 17330867eb

David Rubin <daviru007@icloud.com>
2025-02-19 18:46:30
Sema: compile error on reifying align(0) struct fields
1 parent a00edbd
src/Sema.zig
@@ -2649,7 +2649,13 @@ pub fn analyzeAsAlign(
     src: LazySrcLoc,
     air_ref: Air.Inst.Ref,
 ) !Alignment {
-    const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty, .{ .simple = .@"align" });
+    const alignment_big = try sema.analyzeAsInt(
+        block,
+        src,
+        air_ref,
+        align_ty,
+        .{ .simple = .@"align" },
+    );
     return sema.validateAlign(block, src, alignment_big);
 }
 
@@ -18807,7 +18813,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     const abi_align: Alignment = if (inst_data.flags.has_align) blk: {
         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
         extra_i += 1;
-        const coerced = try sema.coerce(block, .u32, try sema.resolveInst(ref), align_src);
+        const coerced = try sema.coerce(block, align_ty, try sema.resolveInst(ref), align_src);
         const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ .simple = .@"align" });
         // Check if this happens to be the lazy alignment of our element type, in
         // which case we can make this 0 without resolving it.
@@ -20325,15 +20331,11 @@ fn zirReify(
                 try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls),
             ).?);
 
-            if (!try sema.intFitsInType(alignment_val, .u32, null)) {
-                return sema.fail(block, src, "alignment must fit in 'u32'", .{});
+            if (!try sema.intFitsInType(alignment_val, align_ty, null)) {
+                return sema.fail(block, src, "alignment must fit in '{}'", .{align_ty.fmt(pt)});
             }
-
             const alignment_val_int = try alignment_val.toUnsignedIntSema(pt);
-            if (alignment_val_int > 0 and !math.isPowerOfTwo(alignment_val_int)) {
-                return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{alignment_val_int});
-            }
-            const abi_align = Alignment.fromByteUnits(alignment_val_int);
+            const abi_align = try sema.validateAlign(block, src, alignment_val_int);
 
             const elem_ty = child_val.toType();
             if (abi_align != .none) {
@@ -21017,11 +21019,7 @@ fn reifyUnion(
             field_ty.* = field_type_val.toIntern();
             if (any_aligns) {
                 const byte_align = try (try field_info.fieldValue(pt, 2)).toUnsignedIntSema(pt);
-                if (byte_align > 0 and !math.isPowerOfTwo(byte_align)) {
-                    // TODO: better source location
-                    return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align});
-                }
-                field_aligns[field_idx] = Alignment.fromByteUnits(byte_align);
+                field_aligns[field_idx] = try sema.validateAlign(block, src, byte_align);
             }
         }
 
@@ -21062,11 +21060,7 @@ fn reifyUnion(
             field_ty.* = field_type_val.toIntern();
             if (any_aligns) {
                 const byte_align = try (try field_info.fieldValue(pt, 2)).toUnsignedIntSema(pt);
-                if (byte_align > 0 and !math.isPowerOfTwo(byte_align)) {
-                    // TODO: better source location
-                    return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align});
-                }
-                field_aligns[field_idx] = Alignment.fromByteUnits(byte_align);
+                field_aligns[field_idx] = try sema.validateAlign(block, src, byte_align);
             }
         }
 
@@ -21266,7 +21260,6 @@ fn reifyStruct(
 
     var any_comptime_fields = false;
     var any_default_inits = false;
-    var any_aligned_fields = false;
 
     for (0..fields_len) |field_idx| {
         const field_info = try fields_val.elemValue(pt, field_idx);
@@ -21301,11 +21294,6 @@ fn reifyStruct(
 
         if (field_is_comptime) any_comptime_fields = true;
         if (field_default_value != .none) any_default_inits = true;
-        switch (try field_alignment_val.orderAgainstZeroSema(pt)) {
-            .eq => {},
-            .gt => any_aligned_fields = true,
-            .lt => unreachable,
-        }
     }
 
     const tracked_inst = try block.trackZir(inst);
@@ -21317,7 +21305,7 @@ fn reifyStruct(
         .requires_comptime = .unknown,
         .any_comptime_fields = any_comptime_fields,
         .any_default_inits = any_default_inits,
-        .any_aligned_fields = any_aligned_fields,
+        .any_aligned_fields = true,
         .inits_resolved = true,
         .key = .{ .reified = .{
             .zir_index = tracked_inst,
@@ -21361,21 +21349,14 @@ fn reifyStruct(
             return sema.fail(block, src, "duplicate struct field name {f}", .{field_name.fmt(ip)});
         }
 
-        if (any_aligned_fields) {
-            if (!try sema.intFitsInType(field_alignment_val, .u32, null)) {
-                return sema.fail(block, src, "alignment must fit in 'u32'", .{});
-            }
-
-            const byte_align = try field_alignment_val.toUnsignedIntSema(pt);
-            if (byte_align == 0) {
-                if (layout != .@"packed") {
-                    struct_type.field_aligns.get(ip)[field_idx] = .none;
-                }
-            } else {
-                if (layout == .@"packed") return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
-                if (!math.isPowerOfTwo(byte_align)) return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align});
-                struct_type.field_aligns.get(ip)[field_idx] = Alignment.fromNonzeroByteUnits(byte_align);
-            }
+        if (!try sema.intFitsInType(field_alignment_val, align_ty, null)) {
+            return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)});
+        }
+        const byte_align = try field_alignment_val.toUnsignedIntSema(pt);
+        if (layout == .@"packed") {
+            if (byte_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
+        } else {
+            struct_type.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align);
         }
 
         const field_is_comptime = field_is_comptime_val.toBool();
test/cases/compile_errors/align_zero.zig
@@ -1,52 +1,98 @@
-pub var global_var: i32 align(0) = undefined;
+var global_var: i32 align(0) = undefined;
 
-pub export fn a() void {
+export fn a() void {
     _ = &global_var;
 }
 
-pub extern var extern_var: i32 align(0);
+extern var extern_var: i32 align(0);
 
-pub export fn b() void {
+export fn b() void {
     _ = &extern_var;
 }
 
-pub export fn c() align(0) void {}
+export fn c() align(0) void {}
 
-pub export fn d() void {
+export fn d() void {
     _ = *align(0) fn () i32;
 }
 
-pub export fn e() void {
+export fn e() void {
     var local_var: i32 align(0) = undefined;
     _ = &local_var;
 }
 
-pub export fn f() void {
+export fn f() void {
     _ = *align(0) i32;
 }
 
-pub export fn g() void {
+export fn g() void {
     _ = []align(0) i32;
 }
 
-pub export fn h() void {
+export fn h() void {
     _ = struct { field: i32 align(0) };
 }
 
-pub export fn i() void {
+export fn i() void {
     _ = union { field: i32 align(0) };
 }
 
+export fn j() void {
+    _ = @Type(.{ .@"struct" = .{
+        .layout = .auto,
+        .fields = &.{.{
+            .name = "test",
+            .type = u32,
+            .default_value_ptr = null,
+            .is_comptime = false,
+            .alignment = 0,
+        }},
+        .decls = &.{},
+        .is_tuple = false,
+    } });
+}
+
+export fn k() void {
+    _ = @Type(.{ .pointer = .{
+        .size = .one,
+        .is_const = false,
+        .is_volatile = false,
+        .alignment = 0,
+        .address_space = .generic,
+        .child = u32,
+        .is_allowzero = false,
+        .sentinel_ptr = null,
+    } });
+}
+
+export fn l() void {
+    _ = @Type(.{ .@"struct" = .{
+        .layout = .@"packed",
+        .fields = &.{.{
+            .name = "test",
+            .type = u32,
+            .default_value_ptr = null,
+            .is_comptime = false,
+            .alignment = 8,
+        }},
+        .decls = &.{},
+        .is_tuple = false,
+    } });
+}
+
 // error
 // backend=stage2
 // target=native
 //
-// :1:31: error: alignment must be >= 1
-// :7:38: error: alignment must be >= 1
-// :13:25: error: alignment must be >= 1
+// :1:27: error: alignment must be >= 1
+// :7:34: error: alignment must be >= 1
+// :13:21: error: alignment must be >= 1
 // :16:16: error: alignment must be >= 1
 // :20:30: error: alignment must be >= 1
 // :25:16: error: alignment must be >= 1
 // :29:17: error: alignment must be >= 1
 // :33:35: error: alignment must be >= 1
 // :37:34: error: alignment must be >= 1
+// :41:9: error: alignment can only be 0 on packed struct fields
+// :56:9: error: alignment must be >= 1
+// :69:9: error: alignment in a packed struct field must be set to 0
test/cases/compile_errors/bad_alignment_type.zig
@@ -11,5 +11,5 @@ export fn entry2() void {
 // backend=stage2
 // target=native
 //
-// :2:22: error: expected type 'u32', found 'bool'
-// :6:21: error: fractional component prevents float value '12.34' from coercion to type 'u32'
+// :2:22: error: expected type 'u29', found 'bool'
+// :6:21: error: fractional component prevents float value '12.34' from coercion to type 'u29'
test/cases/compile_errors/reify_type_with_invalid_field_alignment.zig
@@ -43,6 +43,6 @@ comptime {
 
 // error
 //
-// :2:9: error: alignment value '3' is not a power of two or zero
-// :14:9: error: alignment value '5' is not a power of two or zero
-// :30:9: error: alignment value '7' is not a power of two or zero
+// :2:9: error: alignment value '3' is not a power of two
+// :14:9: error: alignment value '5' is not a power of two
+// :30:9: error: alignment value '7' is not a power of two