commit 0bcaad394ddbc629ab0b38aebbe90545ea3e9bab (tree)
parent a3ae499dc29747630f2801aab9bbc9661a4a0169
Author: Justus Klausecker <justusk@noreply.codeberg.org>
Date: Tue, 16 Jun 2026 11:51:54 +0200
Merge pull request 'Sema: make struct fields referencing comptime vars not comptime' (#31462) from rmehri01/zig:struct_comptime_var_ref into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31462
Reviewed-by: Justus Klausecker <justusk@noreply.codeberg.org>
Diffstat:
3 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/src/Sema.zig b/src/Sema.zig
@@ -18777,6 +18777,18 @@ fn structInitAnon(
break :rs runtime_index;
};
+ // A field can't be `comptime` if it references a `comptime var` but the aggregate can still be comptime-known.
+ // Replace these fields with `.none` only for generating the type.
+ const values_no_comptime = if (!any_values) values else blk: {
+ const new_values = try sema.arena.alloc(InternPool.Index, types.len);
+ for (values, new_values) |val, *new_val| {
+ if (val != .none and Value.fromInterned(val).canMutateComptimeVarState(zcu)) {
+ new_val.* = .none;
+ } else new_val.* = val;
+ }
+ break :blk new_values;
+ };
+
// We treat anonymous struct types as reified types, because there are similarities: they have
// no captures, and instead use a form of structural equivalence which we can easy represent by
// hashing the field names/types/values. They also perform layout resolution immediately. These
@@ -18785,7 +18797,7 @@ fn structInitAnon(
const type_hash: u64 = hash: {
var hasher = std.hash.Wyhash.init(0);
hasher.update(std.mem.sliceAsBytes(types));
- hasher.update(std.mem.sliceAsBytes(values));
+ hasher.update(std.mem.sliceAsBytes(values_no_comptime));
hasher.update(std.mem.sliceAsBytes(names));
break :hash hasher.final();
};
@@ -18809,9 +18821,9 @@ fn structInitAnon(
@memcpy(wip.field_names.get(ip), names);
@memcpy(wip.field_types.get(ip), types);
if (any_values) {
- @memcpy(wip.field_values.get(ip), values);
+ @memcpy(wip.field_values.get(ip), values_no_comptime);
@memset(wip.field_is_comptime_bits.getAll(ip), 0);
- for (values, 0..) |val, field_index| {
+ for (values_no_comptime, 0..) |val, field_index| {
if (val == .none) continue;
const bit_bag_index = field_index / 32;
const mask = @as(u32, 1) << @intCast(field_index % 32);
@@ -18839,6 +18851,15 @@ fn structInitAnon(
return sema.addConstantMaybeRef(struct_val, is_ref);
};
+ for (values, 0..) |field_val, i| {
+ if (field_val == .none) continue; // runtime-known
+ const field_src = block.src(.{ .init_elem = .{
+ .init_node_offset = src.offset.node_offset.x,
+ .elem_index = @intCast(i),
+ } });
+ try sema.validateRuntimeValue(block, field_src, .fromIntern(field_val));
+ }
+
if (is_ref) {
const target = zcu.getTarget();
const alloc_ty = try pt.ptrType(.{
@@ -19095,13 +19116,11 @@ fn arrayInitAnon(
.values = values_no_comptime,
}));
- const runtime_src = opt_runtime_src orelse {
+ _ = opt_runtime_src orelse {
const tuple_val = try pt.aggregateValue(tuple_ty, values);
return sema.addConstantMaybeRef(tuple_val, is_ref);
};
- try sema.requireRuntimeBlock(block, src, runtime_src);
-
for (operands, 0..) |operand, i| {
const operand_src = block.src(.{ .init_elem = .{
.init_node_offset = src.offset.node_offset.x,
diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig
@@ -2326,3 +2326,12 @@ test "pointer to runtime field of struct containing struct containing comptime-o
ptr = &foo.number;
try expect(ptr.* == 123);
}
+
+test "struct field referencing comptime var isn't comptime" {
+ comptime var v: u8 = 0;
+ const s = .{ .v = &v };
+ // field isn't comptime but struct is still comptime-known
+ comptime assert(!@typeInfo(@TypeOf(s)).@"struct".field_attrs[0].@"comptime");
+ v = 1;
+ comptime assert(s.v.* == 1);
+}
diff --git a/test/cases/compile_errors/comptime_var_referenced_at_runtime.zig b/test/cases/compile_errors/comptime_var_referenced_at_runtime.zig
@@ -71,6 +71,12 @@ export fn bax() void {
@memmove(&rt, &x);
}
+export fn qoo(i: u8) void {
+ comptime var x: u32 = 123;
+ const y = .{ .p = &x, .i = i };
+ _ = y;
+}
+
// error
//
// :5:19: error: runtime value contains reference to comptime var
@@ -103,3 +109,6 @@ export fn bax() void {
// :71:19: error: runtime value contains reference to comptime var
// :71:19: note: comptime var pointers are not available at runtime
// :67:14: note: 'runtime_value' points to comptime var declared here
+// :76:19: error: runtime value contains reference to comptime var
+// :76:19: note: comptime var pointers are not available at runtime
+// :75:14: note: 'runtime_value' points to comptime var declared here