Skip to content

Sema: fix pointers to comptime fields of comptime-known aggregate pointers #23263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28013,12 +28013,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);
Expand All @@ -28038,6 +28043,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;
Expand All @@ -28048,6 +28054,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.
Expand All @@ -28071,7 +28078,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(),
Expand Down Expand Up @@ -28610,7 +28617,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);

Expand All @@ -28628,9 +28636,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);
},
},
});

Expand Down
42 changes: 18 additions & 24 deletions src/codegen/c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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", .{
Expand Down Expand Up @@ -4045,7 +4045,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;
Expand Down Expand Up @@ -4516,7 +4516,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;
Expand Down Expand Up @@ -4934,7 +4934,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)});
}
Expand Down Expand Up @@ -4979,14 +4979,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;
Expand Down Expand Up @@ -5698,7 +5692,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;
},
Expand All @@ -5718,7 +5712,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;
Expand Down Expand Up @@ -5844,7 +5838,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);

Expand Down Expand Up @@ -5898,7 +5892,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);
Expand Down Expand Up @@ -6021,7 +6015,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;
Expand Down Expand Up @@ -6119,7 +6113,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;
}
Expand Down Expand Up @@ -6164,7 +6158,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;
Expand Down Expand Up @@ -6365,7 +6359,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;
Expand All @@ -6382,7 +6376,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);
}
Expand Down Expand Up @@ -6912,7 +6906,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(" != ");
Expand Down Expand Up @@ -7294,7 +7288,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);
Expand Down
18 changes: 18 additions & 0 deletions test/behavior/tuple.zig
Original file line number Diff line number Diff line change
Expand Up @@ -602,3 +602,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();
}
19 changes: 19 additions & 0 deletions test/cases/compile_errors/runtime_store_to_comptime_field.zig
Original file line number Diff line number Diff line change
@@ -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