Commit 2c0aa1c6f5

xdBronch <51252236+xdBronch@users.noreply.github.com>
2025-10-02 15:26:54
don't make anonymous tuple fields referencing `comptime var`s `comptime`
1 parent 27aba2d
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -20048,10 +20048,14 @@ fn arrayInitAnon(
     const types = try sema.arena.alloc(InternPool.Index, operands.len);
     const values = try sema.arena.alloc(InternPool.Index, operands.len);
 
+    var any_comptime = false;
     const opt_runtime_src = rs: {
         var runtime_src: ?LazySrcLoc = null;
         for (operands, 0..) |operand, i| {
-            const operand_src = src; // TODO better source location
+            const operand_src = block.src(.{ .init_elem = .{
+                .init_node_offset = src.offset.node_offset.x,
+                .elem_index = @intCast(i),
+            } });
             const elem = try sema.resolveInst(operand);
             types[i] = sema.typeOf(elem).toIntern();
             if (Type.fromInterned(types[i]).zigTypeTag(zcu) == .@"opaque") {
@@ -20066,6 +20070,7 @@ fn arrayInitAnon(
             }
             if (try sema.resolveValue(elem)) |val| {
                 values[i] = val.toIntern();
+                any_comptime = true;
             } else {
                 values[i] = .none;
                 runtime_src = operand_src;
@@ -20074,9 +20079,21 @@ fn arrayInitAnon(
         break :rs runtime_src;
     };
 
+    // 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_comptime) values else blk: {
+        const new_values = try sema.arena.alloc(InternPool.Index, operands.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;
+    };
+
     const tuple_ty: Type = .fromInterned(try ip.getTupleType(gpa, pt.tid, .{
         .types = types,
-        .values = values,
+        .values = values_no_comptime,
     }));
 
     const runtime_src = opt_runtime_src orelse {
@@ -20086,6 +20103,14 @@ fn arrayInitAnon(
 
     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,
+            .elem_index = @intCast(i),
+        } });
+        try sema.validateRuntimeValue(block, operand_src, try sema.resolveInst(operand));
+    }
+
     if (is_ref) {
         const target = sema.pt.zcu.getTarget();
         const alloc_ty = try pt.ptrTypeSema(.{
test/behavior/tuple.zig
@@ -486,6 +486,15 @@ test "tuple with comptime fields with non empty initializer" {
     _ = a;
 }
 
+test "anon tuple field referencing comptime var isn't comptime" {
+    comptime var a: u8 = 0;
+    const tuple = .{&a};
+    // field isn't comptime but tuple is still comptime-known
+    comptime assert(@TypeOf(tuple) == struct { *u8 });
+    a = 1;
+    comptime assert(tuple[0].* == 1);
+}
+
 test "tuple with runtime value coerced into a slice with a sentinel" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO