From e07d8fccd1389f4d9da4f5d906c8349d3014add8 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Sat, 3 May 2025 20:10:42 +0100 Subject: [PATCH] Merge pull request #23263 from mlugg/comptime-field-ptr Sema: fix pointers to comptime fields of comptime-known aggregate pointers --- src/Sema.zig | 33 +++++++++++---- src/codegen/c.zig | 42 ++++++++----------- test/behavior/tuple.zig | 18 ++++++++ .../runtime_store_to_comptime_field.zig | 19 +++++++++ 4 files changed, 79 insertions(+), 33 deletions(-) create mode 100644 test/cases/compile_errors/runtime_store_to_comptime_field.zig diff --git a/src/Sema.zig b/src/Sema.zig index 354cb9e4d1..d4284aa441 100644 --- a/src/Sema.zig +++ b/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); + }, }, }); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 2aee078b11..6e2e5c35af 100644 --- a/src/codegen/c.zig +++ b/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); diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 0a0ed1d620..d014d9cf97 100644 --- a/test/behavior/tuple.zig +++ b/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(); +} diff --git a/test/cases/compile_errors/runtime_store_to_comptime_field.zig b/test/cases/compile_errors/runtime_store_to_comptime_field.zig new file mode 100644 index 0000000000..0c5d6a7ad3 --- /dev/null +++ b/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