Commit e07d8fccd1

Matthew Lugg <mlugg@mlugg.co.uk>
2025-05-03 21:10:42
Merge pull request #23263 from mlugg/comptime-field-ptr
Sema: fix pointers to comptime fields of comptime-known aggregate pointers
1 parent db936b9
Changed files (4)
src
test
behavior
cases
src/codegen/c.zig
@@ -611,7 +611,7 @@ pub const Function = struct {
         const a = try Assignment.start(f, writer, ctype);
         try f.writeCValue(writer, dst, .Other);
         try a.assign(f, writer);
-        try f.writeCValue(writer, src, .Initializer);
+        try f.writeCValue(writer, src, .Other);
         try a.end(f, writer);
     }
 
@@ -2826,7 +2826,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
                 });
                 try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, .none, .complete);
                 try w.writeAll(" = ");
-                try o.dg.renderValue(w, Value.fromInterned(name_val), .Initializer);
+                try o.dg.renderValue(w, Value.fromInterned(name_val), .StaticInitializer);
                 try w.writeAll(";\n   return (");
                 try o.dg.renderType(w, name_slice_ty);
                 try w.print("){{{}, {}}};\n", .{
@@ -4044,7 +4044,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
             const new_local = try f.allocLocal(inst, src_ty);
             try f.writeCValue(writer, new_local, .Other);
             try writer.writeAll(" = ");
-            try f.writeCValue(writer, src_val, .Initializer);
+            try f.writeCValue(writer, src_val, .Other);
             try writer.writeAll(";\n");
 
             break :blk new_local;
@@ -4515,7 +4515,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
         const a = try Assignment.start(f, writer, .usize);
         try f.writeCValueMember(writer, local, .{ .identifier = "len" });
         try a.assign(f, writer);
-        try f.writeCValue(writer, len, .Initializer);
+        try f.writeCValue(writer, len, .Other);
         try a.end(f, writer);
     }
     return local;
@@ -4933,7 +4933,7 @@ fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
     const cond_local = f.loop_switch_conds.get(br.block_inst).?;
     try f.writeCValue(writer, .{ .local = cond_local }, .Other);
     try writer.writeAll(" = ");
-    try f.writeCValue(writer, cond, .Initializer);
+    try f.writeCValue(writer, cond, .Other);
     try writer.writeAll(";\n");
     try writer.print("goto zig_switch_{d}_loop;", .{@intFromEnum(br.block_inst)});
 }
@@ -4978,14 +4978,8 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
     const operand_lval = if (operand == .constant) blk: {
         const operand_local = try f.allocLocal(null, operand_ty);
         try f.writeCValue(writer, operand_local, .Other);
-        if (operand_ty.isAbiInt(zcu)) {
-            try writer.writeAll(" = ");
-        } else {
-            try writer.writeAll(" = (");
-            try f.renderType(writer, operand_ty);
-            try writer.writeByte(')');
-        }
-        try f.writeCValue(writer, operand, .Initializer);
+        try writer.writeAll(" = ");
+        try f.writeCValue(writer, operand, .Other);
         try writer.writeAll(";\n");
         break :blk operand_local;
     } else operand;
@@ -5697,7 +5691,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
             const a = try Assignment.start(f, writer, opt_ctype);
             try f.writeCValueDeref(writer, operand);
             try a.assign(f, writer);
-            try f.object.dg.renderValue(writer, Value.false, .Initializer);
+            try f.object.dg.renderValue(writer, Value.false, .Other);
             try a.end(f, writer);
             return .none;
         },
@@ -5717,7 +5711,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
                 const a = try Assignment.start(f, writer, opt_ctype);
                 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "is_null" });
                 try a.assign(f, writer);
-                try f.object.dg.renderValue(writer, Value.false, .Initializer);
+                try f.object.dg.renderValue(writer, Value.false, .Other);
                 try a.end(f, writer);
             }
             if (f.liveness.isUnused(inst)) return .none;
@@ -5843,7 +5837,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
     try writer.writeByte(')');
 
     switch (fieldLocation(container_ptr_ty, field_ptr_ty, extra.field_index, pt)) {
-        .begin => try f.writeCValue(writer, field_ptr_val, .Initializer),
+        .begin => try f.writeCValue(writer, field_ptr_val, .Other),
         .field => |field| {
             const u8_ptr_ty = try pt.adjustPtrTypeChild(field_ptr_ty, .u8);
 
@@ -5897,7 +5891,7 @@ fn fieldPtr(
     try writer.writeByte(')');
 
     switch (fieldLocation(container_ptr_ty, field_ptr_ty, field_index, pt)) {
-        .begin => try f.writeCValue(writer, container_ptr_val, .Initializer),
+        .begin => try f.writeCValue(writer, container_ptr_val, .Other),
         .field => |field| {
             try writer.writeByte('&');
             try f.writeCValueDerefMember(writer, container_ptr_val, field);
@@ -6020,7 +6014,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
                         const operand_local = try f.allocLocal(inst, struct_ty);
                         try f.writeCValue(writer, operand_local, .Other);
                         try writer.writeAll(" = ");
-                        try f.writeCValue(writer, struct_byval, .Initializer);
+                        try f.writeCValue(writer, struct_byval, .Other);
                         try writer.writeAll(";\n");
                         break :blk operand_local;
                     } else struct_byval;
@@ -6118,7 +6112,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
         try writer.writeAll(" = (");
         try f.renderType(writer, inst_ty);
         try writer.writeByte(')');
-        try f.writeCValue(writer, operand, .Initializer);
+        try f.writeCValue(writer, operand, .Other);
         try writer.writeAll(";\n");
         return local;
     }
@@ -6163,7 +6157,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
                     const a = try Assignment.start(f, writer, operand_ctype);
                     try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
                     try a.assign(f, writer);
-                    try f.writeCValue(writer, operand, .Initializer);
+                    try f.writeCValue(writer, operand, .Other);
                     try a.end(f, writer);
                 }
                 return local;
@@ -6364,7 +6358,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
         try f.writeCValueMember(writer, local, .{ .identifier = "ptr" });
         try a.assign(f, writer);
         if (operand == .undef) {
-            try f.writeCValue(writer, .{ .undef = inst_ty.slicePtrFieldType(zcu) }, .Initializer);
+            try f.writeCValue(writer, .{ .undef = inst_ty.slicePtrFieldType(zcu) }, .Other);
         } else {
             const ptr_ctype = try f.ctypeFromType(ptr_ty, .complete);
             const ptr_child_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype;
@@ -6381,7 +6375,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
                 try writer.writeByte('&');
                 try f.writeCValueDeref(writer, operand);
                 try writer.print("[{}]", .{try f.fmtIntLiteral(try pt.intValue(.usize, 0))});
-            } else try f.writeCValue(writer, operand, .Initializer);
+            } else try f.writeCValue(writer, operand, .Other);
         }
         try a.end(f, writer);
     }
@@ -6911,7 +6905,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
         try writer.writeAll("for (");
         try f.writeCValue(writer, index, .Other);
         try writer.writeAll(" = ");
-        try f.object.dg.renderValue(writer, try pt.intValue(.usize, 0), .Initializer);
+        try f.object.dg.renderValue(writer, try pt.intValue(.usize, 0), .Other);
         try writer.writeAll("; ");
         try f.writeCValue(writer, index, .Other);
         try writer.writeAll(" != ");
@@ -7281,7 +7275,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
             .float => try pt.floatValue(scalar_ty, std.math.nan(f128)),
             else => unreachable,
         },
-    }, .Initializer);
+    }, .Other);
     try writer.writeAll(";\n");
 
     const v = try Vectorize.start(f, inst, writer, operand_ty);
src/Sema.zig
@@ -28582,12 +28582,17 @@ fn structFieldPtrByIndex(
     const zcu = pt.zcu;
     const ip = &zcu.intern_pool;
 
-    if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
-        const val = try struct_ptr_val.ptrField(field_index, pt);
-        return Air.internedToRef(val.toIntern());
+    const struct_type = zcu.typeToStruct(struct_ty).?;
+    const field_is_comptime = struct_type.fieldIsComptime(ip, field_index);
+
+    // Comptime fields are handled later
+    if (!field_is_comptime) {
+        if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
+            const val = try struct_ptr_val.ptrField(field_index, pt);
+            return Air.internedToRef(val.toIntern());
+        }
     }
 
-    const struct_type = zcu.typeToStruct(struct_ty).?;
     const field_ty = struct_type.field_types.get(ip)[field_index];
     const struct_ptr_ty = sema.typeOf(struct_ptr);
     const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu);
@@ -28607,6 +28612,7 @@ fn structFieldPtrByIndex(
         try Type.fromInterned(struct_ptr_ty_info.child).abiAlignmentSema(pt);
 
     if (struct_type.layout == .@"packed") {
+        assert(!field_is_comptime);
         switch (struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt)) {
             .bit_ptr => |packed_offset| {
                 ptr_ty_data.flags.alignment = parent_align;
@@ -28617,6 +28623,7 @@ fn structFieldPtrByIndex(
             },
         }
     } else if (struct_type.layout == .@"extern") {
+        assert(!field_is_comptime);
         // For extern structs, field alignment might be bigger than type's
         // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the
         // second field is aligned as u32.
@@ -28640,7 +28647,7 @@ fn structFieldPtrByIndex(
 
     const ptr_field_ty = try pt.ptrTypeSema(ptr_ty_data);
 
-    if (struct_type.fieldIsComptime(ip, field_index)) {
+    if (field_is_comptime) {
         try struct_ty.resolveStructFieldInits(pt);
         const val = try pt.intern(.{ .ptr = .{
             .ty = ptr_field_ty.toIntern(),
@@ -29173,7 +29180,8 @@ fn tupleFieldPtr(
     const pt = sema.pt;
     const zcu = pt.zcu;
     const tuple_ptr_ty = sema.typeOf(tuple_ptr);
-    const tuple_ty = tuple_ptr_ty.childType(zcu);
+    const tuple_ptr_info = tuple_ptr_ty.ptrInfo(zcu);
+    const tuple_ty: Type = .fromInterned(tuple_ptr_info.child);
     try tuple_ty.resolveFields(pt);
     const field_count = tuple_ty.structFieldCount(zcu);
 
@@ -29191,9 +29199,16 @@ fn tupleFieldPtr(
     const ptr_field_ty = try pt.ptrTypeSema(.{
         .child = field_ty.toIntern(),
         .flags = .{
-            .is_const = !tuple_ptr_ty.ptrIsMutable(zcu),
-            .is_volatile = tuple_ptr_ty.isVolatilePtr(zcu),
-            .address_space = tuple_ptr_ty.ptrAddressSpace(zcu),
+            .is_const = tuple_ptr_info.flags.is_const,
+            .is_volatile = tuple_ptr_info.flags.is_volatile,
+            .address_space = tuple_ptr_info.flags.address_space,
+            .alignment = a: {
+                if (tuple_ptr_info.flags.alignment == .none) break :a .none;
+                // The tuple pointer isn't naturally aligned, so the field pointer might be underaligned.
+                const tuple_align = tuple_ptr_info.flags.alignment;
+                const field_align = try field_ty.abiAlignmentSema(pt);
+                break :a tuple_align.min(field_align);
+            },
         },
     });
 
test/behavior/tuple.zig
@@ -603,3 +603,21 @@ test "empty union in tuple" {
     try std.testing.expectEqualStrings("0", info.@"struct".fields[0].name);
     try std.testing.expect(@typeInfo(info.@"struct".fields[0].type) == .@"union");
 }
+
+test "field pointer of underaligned tuple" {
+    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+    const S = struct {
+        fn doTheTest() !void {
+            const T = struct { u8, u32 };
+            var val: T align(2) = .{ 1, 2 };
+
+            comptime assert(@TypeOf(&val[0]) == *u8); // `u8` field pointer isn't overaligned
+            comptime assert(@TypeOf(&val[1]) == *align(2) u32); // `u32` field pointer is correctly underaligned
+
+            try expect(val[0] == 1);
+            try expect(val[1] == 2);
+        }
+    };
+    try S.doTheTest();
+    try comptime S.doTheTest();
+}
test/cases/compile_errors/runtime_store_to_comptime_field.zig
@@ -0,0 +1,19 @@
+const init: u32 = 1;
+fn rt() u32 {
+    return 3;
+}
+
+var tuple_val = .{init};
+export fn tuple_field() void {
+    tuple_val[0] = rt();
+}
+
+var struct_val = .{ .x = init };
+export fn struct_field() void {
+    struct_val.x = rt();
+}
+
+// error
+//
+// :8:14: error: cannot store runtime value in compile time variable
+// :13:15: error: cannot store runtime value in compile time variable