authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-05-03 20:10:42+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-05-03 20:10:42+01:00
logf4e9846bca69e20f907384cdad43b86a3aae1fb2
tree82d5ed3d6b07bc84c1b7cf1033b01bf0f14e50bd
parentf83fe2714bd4441610156e1a6017d07409ad6093
parent81277b5487e53d3e96351c2f1b14f437321210cc
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #23263 from mlugg/comptime-field-ptr

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

4 files changed, 79 insertions(+), 33 deletions(-)

src/Sema.zig+24-9
...@@ -27997,12 +27997,17 @@ fn structFieldPtrByIndex(...@@ -27997,12 +27997,17 @@ fn structFieldPtrByIndex(
27997 const zcu = pt.zcu;27997 const zcu = pt.zcu;
27998 const ip = &zcu.intern_pool;27998 const ip = &zcu.intern_pool;
2799927999
28000 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {28000 const struct_type = zcu.typeToStruct(struct_ty).?;
28001 const val = try struct_ptr_val.ptrField(field_index, pt);28001 const field_is_comptime = struct_type.fieldIsComptime(ip, field_index);
28002 return Air.internedToRef(val.toIntern());28002
28003 // Comptime fields are handled later
28004 if (!field_is_comptime) {
28005 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
28006 const val = try struct_ptr_val.ptrField(field_index, pt);
28007 return Air.internedToRef(val.toIntern());
28008 }
28003 }28009 }
2800428010
28005 const struct_type = zcu.typeToStruct(struct_ty).?;
28006 const field_ty = struct_type.field_types.get(ip)[field_index];28011 const field_ty = struct_type.field_types.get(ip)[field_index];
28007 const struct_ptr_ty = sema.typeOf(struct_ptr);28012 const struct_ptr_ty = sema.typeOf(struct_ptr);
28008 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu);28013 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu);
...@@ -28022,6 +28027,7 @@ fn structFieldPtrByIndex(...@@ -28022,6 +28027,7 @@ fn structFieldPtrByIndex(
28022 try Type.fromInterned(struct_ptr_ty_info.child).abiAlignmentSema(pt);28027 try Type.fromInterned(struct_ptr_ty_info.child).abiAlignmentSema(pt);
2802328028
28024 if (struct_type.layout == .@"packed") {28029 if (struct_type.layout == .@"packed") {
28030 assert(!field_is_comptime);
28025 switch (struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt)) {28031 switch (struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt)) {
28026 .bit_ptr => |packed_offset| {28032 .bit_ptr => |packed_offset| {
28027 ptr_ty_data.flags.alignment = parent_align;28033 ptr_ty_data.flags.alignment = parent_align;
...@@ -28032,6 +28038,7 @@ fn structFieldPtrByIndex(...@@ -28032,6 +28038,7 @@ fn structFieldPtrByIndex(
28032 },28038 },
28033 }28039 }
28034 } else if (struct_type.layout == .@"extern") {28040 } else if (struct_type.layout == .@"extern") {
28041 assert(!field_is_comptime);
28035 // For extern structs, field alignment might be bigger than type's28042 // For extern structs, field alignment might be bigger than type's
28036 // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the28043 // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the
28037 // second field is aligned as u32.28044 // second field is aligned as u32.
...@@ -28055,7 +28062,7 @@ fn structFieldPtrByIndex(...@@ -28055,7 +28062,7 @@ fn structFieldPtrByIndex(
2805528062
28056 const ptr_field_ty = try pt.ptrTypeSema(ptr_ty_data);28063 const ptr_field_ty = try pt.ptrTypeSema(ptr_ty_data);
2805728064
28058 if (struct_type.fieldIsComptime(ip, field_index)) {28065 if (field_is_comptime) {
28059 try struct_ty.resolveStructFieldInits(pt);28066 try struct_ty.resolveStructFieldInits(pt);
28060 const val = try pt.intern(.{ .ptr = .{28067 const val = try pt.intern(.{ .ptr = .{
28061 .ty = ptr_field_ty.toIntern(),28068 .ty = ptr_field_ty.toIntern(),
...@@ -28602,7 +28609,8 @@ fn tupleFieldPtr(...@@ -28602,7 +28609,8 @@ fn tupleFieldPtr(
28602 const pt = sema.pt;28609 const pt = sema.pt;
28603 const zcu = pt.zcu;28610 const zcu = pt.zcu;
28604 const tuple_ptr_ty = sema.typeOf(tuple_ptr);28611 const tuple_ptr_ty = sema.typeOf(tuple_ptr);
28605 const tuple_ty = tuple_ptr_ty.childType(zcu);28612 const tuple_ptr_info = tuple_ptr_ty.ptrInfo(zcu);
28613 const tuple_ty: Type = .fromInterned(tuple_ptr_info.child);
28606 try tuple_ty.resolveFields(pt);28614 try tuple_ty.resolveFields(pt);
28607 const field_count = tuple_ty.structFieldCount(zcu);28615 const field_count = tuple_ty.structFieldCount(zcu);
2860828616
...@@ -28620,9 +28628,16 @@ fn tupleFieldPtr(...@@ -28620,9 +28628,16 @@ fn tupleFieldPtr(
28620 const ptr_field_ty = try pt.ptrTypeSema(.{28628 const ptr_field_ty = try pt.ptrTypeSema(.{
28621 .child = field_ty.toIntern(),28629 .child = field_ty.toIntern(),
28622 .flags = .{28630 .flags = .{
28623 .is_const = !tuple_ptr_ty.ptrIsMutable(zcu),28631 .is_const = tuple_ptr_info.flags.is_const,
28624 .is_volatile = tuple_ptr_ty.isVolatilePtr(zcu),28632 .is_volatile = tuple_ptr_info.flags.is_volatile,
28625 .address_space = tuple_ptr_ty.ptrAddressSpace(zcu),28633 .address_space = tuple_ptr_info.flags.address_space,
28634 .alignment = a: {
28635 if (tuple_ptr_info.flags.alignment == .none) break :a .none;
28636 // The tuple pointer isn't naturally aligned, so the field pointer might be underaligned.
28637 const tuple_align = tuple_ptr_info.flags.alignment;
28638 const field_align = try field_ty.abiAlignmentSema(pt);
28639 break :a tuple_align.min(field_align);
28640 },
28626 },28641 },
28627 });28642 });
2862828643
src/codegen/c.zig+18-24
...@@ -611,7 +611,7 @@ pub const Function = struct {...@@ -611,7 +611,7 @@ pub const Function = struct {
611 const a = try Assignment.start(f, writer, ctype);611 const a = try Assignment.start(f, writer, ctype);
612 try f.writeCValue(writer, dst, .Other);612 try f.writeCValue(writer, dst, .Other);
613 try a.assign(f, writer);613 try a.assign(f, writer);
614 try f.writeCValue(writer, src, .Initializer);614 try f.writeCValue(writer, src, .Other);
615 try a.end(f, writer);615 try a.end(f, writer);
616 }616 }
617617
...@@ -2826,7 +2826,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn...@@ -2826,7 +2826,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
2826 });2826 });
2827 try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, .none, .complete);2827 try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, .none, .complete);
2828 try w.writeAll(" = ");2828 try w.writeAll(" = ");
2829 try o.dg.renderValue(w, Value.fromInterned(name_val), .Initializer);2829 try o.dg.renderValue(w, Value.fromInterned(name_val), .StaticInitializer);
2830 try w.writeAll(";\n return (");2830 try w.writeAll(";\n return (");
2831 try o.dg.renderType(w, name_slice_ty);2831 try o.dg.renderType(w, name_slice_ty);
2832 try w.print("){{{}, {}}};\n", .{2832 try w.print("){{{}, {}}};\n", .{
...@@ -4045,7 +4045,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {...@@ -4045,7 +4045,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
4045 const new_local = try f.allocLocal(inst, src_ty);4045 const new_local = try f.allocLocal(inst, src_ty);
4046 try f.writeCValue(writer, new_local, .Other);4046 try f.writeCValue(writer, new_local, .Other);
4047 try writer.writeAll(" = ");4047 try writer.writeAll(" = ");
4048 try f.writeCValue(writer, src_val, .Initializer);4048 try f.writeCValue(writer, src_val, .Other);
4049 try writer.writeAll(";\n");4049 try writer.writeAll(";\n");
40504050
4051 break :blk new_local;4051 break :blk new_local;
...@@ -4516,7 +4516,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4516,7 +4516,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
4516 const a = try Assignment.start(f, writer, .usize);4516 const a = try Assignment.start(f, writer, .usize);
4517 try f.writeCValueMember(writer, local, .{ .identifier = "len" });4517 try f.writeCValueMember(writer, local, .{ .identifier = "len" });
4518 try a.assign(f, writer);4518 try a.assign(f, writer);
4519 try f.writeCValue(writer, len, .Initializer);4519 try f.writeCValue(writer, len, .Other);
4520 try a.end(f, writer);4520 try a.end(f, writer);
4521 }4521 }
4522 return local;4522 return local;
...@@ -4934,7 +4934,7 @@ fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {...@@ -4934,7 +4934,7 @@ fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
4934 const cond_local = f.loop_switch_conds.get(br.block_inst).?;4934 const cond_local = f.loop_switch_conds.get(br.block_inst).?;
4935 try f.writeCValue(writer, .{ .local = cond_local }, .Other);4935 try f.writeCValue(writer, .{ .local = cond_local }, .Other);
4936 try writer.writeAll(" = ");4936 try writer.writeAll(" = ");
4937 try f.writeCValue(writer, cond, .Initializer);4937 try f.writeCValue(writer, cond, .Other);
4938 try writer.writeAll(";\n");4938 try writer.writeAll(";\n");
4939 try writer.print("goto zig_switch_{d}_loop;", .{@intFromEnum(br.block_inst)});4939 try writer.print("goto zig_switch_{d}_loop;", .{@intFromEnum(br.block_inst)});
4940}4940}
...@@ -4979,14 +4979,8 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal...@@ -4979,14 +4979,8 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
4979 const operand_lval = if (operand == .constant) blk: {4979 const operand_lval = if (operand == .constant) blk: {
4980 const operand_local = try f.allocLocal(null, operand_ty);4980 const operand_local = try f.allocLocal(null, operand_ty);
4981 try f.writeCValue(writer, operand_local, .Other);4981 try f.writeCValue(writer, operand_local, .Other);
4982 if (operand_ty.isAbiInt(zcu)) {4982 try writer.writeAll(" = ");
4983 try writer.writeAll(" = ");4983 try f.writeCValue(writer, operand, .Other);
4984 } else {
4985 try writer.writeAll(" = (");
4986 try f.renderType(writer, operand_ty);
4987 try writer.writeByte(')');
4988 }
4989 try f.writeCValue(writer, operand, .Initializer);
4990 try writer.writeAll(";\n");4984 try writer.writeAll(";\n");
4991 break :blk operand_local;4985 break :blk operand_local;
4992 } else operand;4986 } else operand;
...@@ -5698,7 +5692,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5698,7 +5692,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
5698 const a = try Assignment.start(f, writer, opt_ctype);5692 const a = try Assignment.start(f, writer, opt_ctype);
5699 try f.writeCValueDeref(writer, operand);5693 try f.writeCValueDeref(writer, operand);
5700 try a.assign(f, writer);5694 try a.assign(f, writer);
5701 try f.object.dg.renderValue(writer, Value.false, .Initializer);5695 try f.object.dg.renderValue(writer, Value.false, .Other);
5702 try a.end(f, writer);5696 try a.end(f, writer);
5703 return .none;5697 return .none;
5704 },5698 },
...@@ -5718,7 +5712,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5718,7 +5712,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
5718 const a = try Assignment.start(f, writer, opt_ctype);5712 const a = try Assignment.start(f, writer, opt_ctype);
5719 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "is_null" });5713 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "is_null" });
5720 try a.assign(f, writer);5714 try a.assign(f, writer);
5721 try f.object.dg.renderValue(writer, Value.false, .Initializer);5715 try f.object.dg.renderValue(writer, Value.false, .Other);
5722 try a.end(f, writer);5716 try a.end(f, writer);
5723 }5717 }
5724 if (f.liveness.isUnused(inst)) return .none;5718 if (f.liveness.isUnused(inst)) return .none;
...@@ -5844,7 +5838,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5844,7 +5838,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5844 try writer.writeByte(')');5838 try writer.writeByte(')');
58455839
5846 switch (fieldLocation(container_ptr_ty, field_ptr_ty, extra.field_index, pt)) {5840 switch (fieldLocation(container_ptr_ty, field_ptr_ty, extra.field_index, pt)) {
5847 .begin => try f.writeCValue(writer, field_ptr_val, .Initializer),5841 .begin => try f.writeCValue(writer, field_ptr_val, .Other),
5848 .field => |field| {5842 .field => |field| {
5849 const u8_ptr_ty = try pt.adjustPtrTypeChild(field_ptr_ty, .u8);5843 const u8_ptr_ty = try pt.adjustPtrTypeChild(field_ptr_ty, .u8);
58505844
...@@ -5898,7 +5892,7 @@ fn fieldPtr(...@@ -5898,7 +5892,7 @@ fn fieldPtr(
5898 try writer.writeByte(')');5892 try writer.writeByte(')');
58995893
5900 switch (fieldLocation(container_ptr_ty, field_ptr_ty, field_index, pt)) {5894 switch (fieldLocation(container_ptr_ty, field_ptr_ty, field_index, pt)) {
5901 .begin => try f.writeCValue(writer, container_ptr_val, .Initializer),5895 .begin => try f.writeCValue(writer, container_ptr_val, .Other),
5902 .field => |field| {5896 .field => |field| {
5903 try writer.writeByte('&');5897 try writer.writeByte('&');
5904 try f.writeCValueDerefMember(writer, container_ptr_val, field);5898 try f.writeCValueDerefMember(writer, container_ptr_val, field);
...@@ -6021,7 +6015,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6021,7 +6015,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
6021 const operand_local = try f.allocLocal(inst, struct_ty);6015 const operand_local = try f.allocLocal(inst, struct_ty);
6022 try f.writeCValue(writer, operand_local, .Other);6016 try f.writeCValue(writer, operand_local, .Other);
6023 try writer.writeAll(" = ");6017 try writer.writeAll(" = ");
6024 try f.writeCValue(writer, struct_byval, .Initializer);6018 try f.writeCValue(writer, struct_byval, .Other);
6025 try writer.writeAll(";\n");6019 try writer.writeAll(";\n");
6026 break :blk operand_local;6020 break :blk operand_local;
6027 } else struct_byval;6021 } else struct_byval;
...@@ -6119,7 +6113,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu...@@ -6119,7 +6113,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
6119 try writer.writeAll(" = (");6113 try writer.writeAll(" = (");
6120 try f.renderType(writer, inst_ty);6114 try f.renderType(writer, inst_ty);
6121 try writer.writeByte(')');6115 try writer.writeByte(')');
6122 try f.writeCValue(writer, operand, .Initializer);6116 try f.writeCValue(writer, operand, .Other);
6123 try writer.writeAll(";\n");6117 try writer.writeAll(";\n");
6124 return local;6118 return local;
6125 }6119 }
...@@ -6164,7 +6158,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6164,7 +6158,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
6164 const a = try Assignment.start(f, writer, operand_ctype);6158 const a = try Assignment.start(f, writer, operand_ctype);
6165 try f.writeCValueMember(writer, local, .{ .identifier = "payload" });6159 try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
6166 try a.assign(f, writer);6160 try a.assign(f, writer);
6167 try f.writeCValue(writer, operand, .Initializer);6161 try f.writeCValue(writer, operand, .Other);
6168 try a.end(f, writer);6162 try a.end(f, writer);
6169 }6163 }
6170 return local;6164 return local;
...@@ -6365,7 +6359,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6365,7 +6359,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
6365 try f.writeCValueMember(writer, local, .{ .identifier = "ptr" });6359 try f.writeCValueMember(writer, local, .{ .identifier = "ptr" });
6366 try a.assign(f, writer);6360 try a.assign(f, writer);
6367 if (operand == .undef) {6361 if (operand == .undef) {
6368 try f.writeCValue(writer, .{ .undef = inst_ty.slicePtrFieldType(zcu) }, .Initializer);6362 try f.writeCValue(writer, .{ .undef = inst_ty.slicePtrFieldType(zcu) }, .Other);
6369 } else {6363 } else {
6370 const ptr_ctype = try f.ctypeFromType(ptr_ty, .complete);6364 const ptr_ctype = try f.ctypeFromType(ptr_ty, .complete);
6371 const ptr_child_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype;6365 const ptr_child_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype;
...@@ -6382,7 +6376,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6382,7 +6376,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
6382 try writer.writeByte('&');6376 try writer.writeByte('&');
6383 try f.writeCValueDeref(writer, operand);6377 try f.writeCValueDeref(writer, operand);
6384 try writer.print("[{}]", .{try f.fmtIntLiteral(try pt.intValue(.usize, 0))});6378 try writer.print("[{}]", .{try f.fmtIntLiteral(try pt.intValue(.usize, 0))});
6385 } else try f.writeCValue(writer, operand, .Initializer);6379 } else try f.writeCValue(writer, operand, .Other);
6386 }6380 }
6387 try a.end(f, writer);6381 try a.end(f, writer);
6388 }6382 }
...@@ -6912,7 +6906,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {...@@ -6912,7 +6906,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
6912 try writer.writeAll("for (");6906 try writer.writeAll("for (");
6913 try f.writeCValue(writer, index, .Other);6907 try f.writeCValue(writer, index, .Other);
6914 try writer.writeAll(" = ");6908 try writer.writeAll(" = ");
6915 try f.object.dg.renderValue(writer, try pt.intValue(.usize, 0), .Initializer);6909 try f.object.dg.renderValue(writer, try pt.intValue(.usize, 0), .Other);
6916 try writer.writeAll("; ");6910 try writer.writeAll("; ");
6917 try f.writeCValue(writer, index, .Other);6911 try f.writeCValue(writer, index, .Other);
6918 try writer.writeAll(" != ");6912 try writer.writeAll(" != ");
...@@ -7282,7 +7276,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7282,7 +7276,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
7282 .float => try pt.floatValue(scalar_ty, std.math.nan(f128)),7276 .float => try pt.floatValue(scalar_ty, std.math.nan(f128)),
7283 else => unreachable,7277 else => unreachable,
7284 },7278 },
7285 }, .Initializer);7279 }, .Other);
7286 try writer.writeAll(";\n");7280 try writer.writeAll(";\n");
72877281
7288 const v = try Vectorize.start(f, inst, writer, operand_ty);7282 const v = try Vectorize.start(f, inst, writer, operand_ty);
test/behavior/tuple.zig+18
...@@ -602,3 +602,21 @@ test "empty union in tuple" {...@@ -602,3 +602,21 @@ test "empty union in tuple" {
602 try std.testing.expectEqualStrings("0", info.@"struct".fields[0].name);602 try std.testing.expectEqualStrings("0", info.@"struct".fields[0].name);
603 try std.testing.expect(@typeInfo(info.@"struct".fields[0].type) == .@"union");603 try std.testing.expect(@typeInfo(info.@"struct".fields[0].type) == .@"union");
604}604}
605
606test "field pointer of underaligned tuple" {
607 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
608 const S = struct {
609 fn doTheTest() !void {
610 const T = struct { u8, u32 };
611 var val: T align(2) = .{ 1, 2 };
612
613 comptime assert(@TypeOf(&val[0]) == *u8); // `u8` field pointer isn't overaligned
614 comptime assert(@TypeOf(&val[1]) == *align(2) u32); // `u32` field pointer is correctly underaligned
615
616 try expect(val[0] == 1);
617 try expect(val[1] == 2);
618 }
619 };
620 try S.doTheTest();
621 try comptime S.doTheTest();
622}
test/cases/compile_errors/runtime_store_to_comptime_field.zig created+19
...@@ -0,0 +1,19 @@
1const init: u32 = 1;
2fn rt() u32 {
3 return 3;
4}
5
6var tuple_val = .{init};
7export fn tuple_field() void {
8 tuple_val[0] = rt();
9}
10
11var struct_val = .{ .x = init };
12export fn struct_field() void {
13 struct_val.x = rt();
14}
15
16// error
17//
18// :8:14: error: cannot store runtime value in compile time variable
19// :13:15: error: cannot store runtime value in compile time variable