Commit a479fd3132

SuperAuguste <19855629+SuperAuguste@users.noreply.github.com>
2023-12-10 11:10:58
Fix some comptime packed struct issues
Co-authored-by: Veikka Tuominen <git@vexu.eu>
1 parent f4f8036
src/Sema.zig
@@ -30341,6 +30341,7 @@ fn storePtrVal(
     var mut_kit = try sema.beginComptimePtrMutation(block, src, ptr_val, operand_ty);
     try sema.checkComptimeVarStore(block, src, mut_kit.mut_decl);
 
+    try sema.resolveTypeLayout(operand_ty);
     switch (mut_kit.pointee) {
         .opv => {},
         .direct => |val_ptr| {
@@ -30355,6 +30356,7 @@ fn storePtrVal(
             val_ptr.* = Value.fromInterned((try operand_val.intern(operand_ty, mod)));
         },
         .reinterpret => |reinterpret| {
+            try sema.resolveTypeLayout(mut_kit.ty);
             const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(mod));
             const buffer = try sema.gpa.alloc(u8, abi_size);
             defer sema.gpa.free(buffer);
@@ -31373,6 +31375,9 @@ fn bitCastUnionFieldVal(
     const mod = sema.mod;
     if (old_ty.eql(field_ty, mod)) return val;
 
+    // Bitcasting a union field value requires that that field's layout be known
+    try sema.resolveTypeLayout(field_ty);
+
     const old_size = try sema.usizeCast(block, src, old_ty.abiSize(mod));
     const field_size = try sema.usizeCast(block, src, field_ty.abiSize(mod));
     const endian = mod.getTarget().cpu.arch.endian();
@@ -35301,7 +35306,10 @@ fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value {
             },
         },
         .un => |un| {
-            const resolved_tag = (try sema.resolveLazyValue(Value.fromInterned(un.tag))).toIntern();
+            const resolved_tag = if (un.tag == .none)
+                .none
+            else
+                (try sema.resolveLazyValue(Value.fromInterned(un.tag))).toIntern();
             const resolved_val = (try sema.resolveLazyValue(Value.fromInterned(un.val))).toIntern();
             return if (resolved_tag == un.tag and resolved_val == un.val)
                 val
src/type.zig
@@ -1607,8 +1607,12 @@ pub const Type = struct {
                 .type_info => unreachable,
             },
             .struct_type => |struct_type| {
-                if (struct_type.layout == .Packed) {
-                    if (opt_sema) |sema| try sema.resolveTypeLayout(ty);
+                const is_packed = struct_type.layout == .Packed;
+                if (opt_sema) |sema| {
+                    try sema.resolveTypeFields(ty);
+                    if (is_packed) try sema.resolveTypeLayout(ty);
+                }
+                if (is_packed) {
                     return try Type.fromInterned(struct_type.backingIntType(ip).*).bitSizeAdvanced(mod, opt_sema);
                 }
                 return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8;
src/value.zig
@@ -847,16 +847,23 @@ pub const Value = struct {
                 // and Extern is handled in non-packed writeToMemory.
                 assert(struct_type.layout == .Packed);
                 var bits: u16 = 0;
-                const storage = ip.indexToKey(val.toIntern()).aggregate.storage;
                 for (0..struct_type.field_types.len) |i| {
+                    const field_val = switch (val.ip_index) {
+                        .none => switch (val.tag()) {
+                            .bytes => unreachable,
+                            .aggregate => val.castTag(.aggregate).?.data[i],
+                            .repeated => val.castTag(.repeated).?.data,
+                            else => unreachable,
+                        },
+                        else => Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
+                            .bytes => unreachable,
+                            .elems => |elems| elems[i],
+                            .repeated_elem => |elem| elem,
+                        }),
+                    };
                     const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
                     const field_bits: u16 = @intCast(field_ty.bitSize(mod));
-                    const field_val = switch (storage) {
-                        .bytes => unreachable,
-                        .elems => |elems| elems[i],
-                        .repeated_elem => |elem| elem,
-                    };
-                    try Value.fromInterned(field_val).writeToPackedMemory(field_ty, mod, buffer, bit_offset + bits);
+                    try field_val.writeToPackedMemory(field_ty, mod, buffer, bit_offset + bits);
                     bits += field_bits;
                 }
             },
test/behavior/packed-struct.zig
@@ -1229,3 +1229,22 @@ test "load flag from packed struct in union" {
     try X.b(&x);
     comptime if (@sizeOf(A) != 1) unreachable;
 }
+
+test "bitcasting a packed struct at comptime and using the result" {
+    comptime {
+        const Struct = packed struct {
+            x: packed union { a: u63, b: i32 },
+            y: u1,
+
+            pub fn bitcast(fd: u64) @This() {
+                return @bitCast(fd);
+            }
+
+            pub fn cannotReach(_: @This()) i32 {
+                return 0;
+            }
+        };
+
+        _ = Struct.bitcast(@as(u64, 0)).cannotReach();
+    }
+}
test/behavior/packed-union.zig
@@ -8,6 +8,11 @@ test "flags in packed union" {
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
 
+    try testFlagsInPackedUnion();
+    try comptime testFlagsInPackedUnion();
+}
+
+fn testFlagsInPackedUnion() !void {
     const FlagBits = packed struct(u8) {
         enable_1: bool = false,
         enable_2: bool = false,
@@ -45,6 +50,11 @@ test "flags in packed union at offset" {
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
 
+    try testFlagsInPackedUnionAtOffset();
+    try comptime testFlagsInPackedUnionAtOffset();
+}
+
+fn testFlagsInPackedUnionAtOffset() !void {
     const FlagBits = packed union {
         base_flags: packed union {
             flags: packed struct(u4) {
@@ -90,6 +100,11 @@ test "packed union in packed struct" {
     // Originally reported at https://github.com/ziglang/zig/issues/16581
     if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
 
+    try testPackedUnionInPackedStruct();
+    try comptime testPackedUnionInPackedStruct();
+}
+
+fn testPackedUnionInPackedStruct() !void {
     const ReadRequest = packed struct { key: i32 };
     const RequestType = enum {
         read,
@@ -142,3 +157,15 @@ test "packed union initialized with a runtime value" {
     } };
     try std.testing.expect((ID{ .value = id.value }).fields.timestamp == timestamp);
 }
+
+test "assigning to non-active field at comptime" {
+    comptime {
+        const FlagBits = packed union {
+            flags: packed struct {},
+            bits: packed struct {},
+        };
+
+        var test_bits: FlagBits = .{ .flags = .{} };
+        test_bits.bits = .{};
+    }
+}