Commit 60be67d3c0

xdBronch <51252236+xdBronch@users.noreply.github.com>
2025-10-04 06:12:26
don't make OPV tuple fields `comptime`
1 parent 60a3324
Changed files (4)
src/Sema.zig
@@ -2824,9 +2824,6 @@ fn zirTupleDecl(
                 }
                 break :init field_init_val.toIntern();
             }
-            if (try sema.typeHasOnePossibleValue(field_type)) |opv| {
-                break :init opv.toIntern();
-            }
             break :init .none;
         };
     }
@@ -21479,18 +21476,8 @@ fn reifyTuple(
             return sema.fail(block, src, "non-comptime tuple fields cannot specify default initialization value", .{});
         }
 
-        const default_or_opv: InternPool.Index = default: {
-            if (field_default_value != .none) {
-                break :default field_default_value;
-            }
-            if (try sema.typeHasOnePossibleValue(field_type)) |opv| {
-                break :default opv.toIntern();
-            }
-            break :default .none;
-        };
-
         field_ty.* = field_type.toIntern();
-        field_init.* = default_or_opv;
+        field_init.* = field_default_value;
     }
 
     return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{
@@ -36282,13 +36269,31 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
                 },
 
                 .tuple_type => |tuple| {
-                    for (tuple.values.get(ip)) |val| {
-                        if (val == .none) return null;
+                    try ty.resolveLayout(pt);
+
+                    if (tuple.types.len == 0) {
+                        return try pt.aggregateValue(ty, &.{});
                     }
-                    // In this case the struct has all comptime-known fields and
-                    // therefore has one possible value.
-                    // TODO: write something like getCoercedInts to avoid needing to dupe
-                    return try pt.aggregateValue(ty, try sema.arena.dupe(InternPool.Index, tuple.values.get(ip)));
+
+                    const field_vals = try sema.arena.alloc(
+                        InternPool.Index,
+                        tuple.types.len,
+                    );
+                    for (
+                        field_vals,
+                        tuple.types.get(ip),
+                        tuple.values.get(ip),
+                    ) |*field_val, field_ty, field_comptime_val| {
+                        if (field_comptime_val != .none) {
+                            field_val.* = field_comptime_val;
+                            continue;
+                        }
+                        if (try sema.typeHasOnePossibleValue(.fromInterned(field_ty))) |opv| {
+                            field_val.* = opv.toIntern();
+                        } else return null;
+                    }
+
+                    return try pt.aggregateValue(ty, field_vals);
                 },
 
                 .union_type => {
src/Type.zig
@@ -2581,15 +2581,30 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
             },
 
             .tuple_type => |tuple| {
-                for (tuple.values.get(ip)) |val| {
-                    if (val == .none) return null;
+                if (tuple.types.len == 0) {
+                    return try pt.aggregateValue(ty, &.{});
                 }
-                // In this case the struct has all comptime-known fields and
-                // therefore has one possible value.
-                // TODO: write something like getCoercedInts to avoid needing to dupe
-                const duped_values = try zcu.gpa.dupe(InternPool.Index, tuple.values.get(ip));
-                defer zcu.gpa.free(duped_values);
-                return try pt.aggregateValue(ty, duped_values);
+
+                const field_vals = try zcu.gpa.alloc(
+                    InternPool.Index,
+                    tuple.types.len,
+                );
+                defer zcu.gpa.free(field_vals);
+                for (
+                    field_vals,
+                    tuple.types.get(ip),
+                    tuple.values.get(ip),
+                ) |*field_val, field_ty, field_comptime_val| {
+                    if (field_comptime_val != .none) {
+                        field_val.* = field_comptime_val;
+                        continue;
+                    }
+                    if (try Type.fromInterned(field_ty).onePossibleValue(pt)) |opv| {
+                        field_val.* = opv.toIntern();
+                    } else return null;
+                }
+
+                return try pt.aggregateValue(ty, field_vals);
             },
 
             .union_type => {
@@ -2630,24 +2645,22 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
                     .auto, .explicit => {
                         if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(zcu)) return null;
 
-                        switch (enum_type.names.len) {
-                            0 => {
-                                const only = try pt.intern(.{ .empty_enum_value = ty.toIntern() });
-                                return Value.fromInterned(only);
-                            },
-                            1 => {
-                                if (enum_type.values.len == 0) {
-                                    const only = try pt.intern(.{ .enum_tag = .{
-                                        .ty = ty.toIntern(),
-                                        .int = (try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern(),
-                                    } });
-                                    return Value.fromInterned(only);
-                                } else {
-                                    return Value.fromInterned(enum_type.values.get(ip)[0]);
-                                }
-                            },
+                        return Value.fromInterned(switch (enum_type.names.len) {
+                            0 => try pt.intern(.{ .empty_enum_value = ty.toIntern() }),
+                            1 => try pt.intern(.{ .enum_tag = .{
+                                .ty = ty.toIntern(),
+                                .int = if (enum_type.values.len == 0)
+                                    (try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern()
+                                else
+                                    try ip.getCoercedInts(
+                                        zcu.gpa,
+                                        pt.tid,
+                                        ip.indexToKey(enum_type.values.get(ip)[0]).int,
+                                        enum_type.tag_ty,
+                                    ),
+                            } }),
                             else => return null,
-                        }
+                        });
                     },
                 }
             },
test/behavior/tuple.zig
@@ -611,3 +611,24 @@ test "field pointer of underaligned tuple" {
     try S.doTheTest();
     try comptime S.doTheTest();
 }
+
+test "OPV tuple fields aren't comptime" {
+    const T = struct { void };
+    const t_info = @typeInfo(T);
+    try expect(!t_info.@"struct".fields[0].is_comptime);
+
+    const T2 = @Type(.{ .@"struct" = .{
+        .layout = .auto,
+        .fields = &.{.{
+            .name = "0",
+            .type = void,
+            .default_value_ptr = null,
+            .is_comptime = false,
+            .alignment = @alignOf(void),
+        }},
+        .decls = &.{},
+        .is_tuple = true,
+    } });
+    const t2_info = @typeInfo(T2);
+    try expect(!t2_info.@"struct".fields[0].is_comptime);
+}
test/cases/compile_errors/invalid_tuple_to_struct_coercion.zig
@@ -8,5 +8,5 @@ export fn entry() void {
 
 // error
 //
-// :6:31: error: expected type 'tmp.S', found 'struct { comptime void = {} }'
+// :6:31: error: expected type 'tmp.S', found 'struct { void }'
 // :1:11: note: struct declared here