diff --git a/src/Sema.zig b/src/Sema.zig index 2c404c476e..655b9c20a4 100644 --- a/src/Sema.zig +++ b/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 diff --git a/src/type.zig b/src/type.zig index 64a85aaa2d..067deecfea 100644 --- a/src/type.zig +++ b/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; diff --git a/src/value.zig b/src/value.zig index 69ddb56b8f..9bcfad18e1 100644 --- a/src/value.zig +++ b/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; } }, diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 59951495ad..dae967f398 100644 --- a/test/behavior/packed-struct.zig +++ b/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(); + } +} diff --git a/test/behavior/packed-union.zig b/test/behavior/packed-union.zig index e3bf915417..1045319fe4 100644 --- a/test/behavior/packed-union.zig +++ b/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 = .{}; + } +}