| author | |
| committer | |
| log | 80e493cb3631827909da4daaff001edb8691a83f |
| tree | 2a069f4444915c7b6335966230b1d62ae6f9a309 |
| parent | 2611d97fb07e5cba5657f508e93f01d60a2379bd |
| signature |
3 files changed, 109 insertions(+), 3 deletions(-)
src/Module.zig+1| ... | @@ -991,6 +991,7 @@ pub const Struct = struct { | ... | @@ -991,6 +991,7 @@ pub const Struct = struct { |
| 991 | is_comptime: bool, | 991 | is_comptime: bool, |
| 992 | 992 | ||
| 993 | /// Returns the field alignment. If the struct is packed, returns 0. | 993 | /// Returns the field alignment. If the struct is packed, returns 0. |
| 994 | /// Keep implementation in sync with `Sema.structFieldAlignment`. | ||
| 994 | pub fn alignment( | 995 | pub fn alignment( |
| 995 | field: Field, | 996 | field: Field, |
| 996 | mod: *Module, | 997 | mod: *Module, |
src/Sema.zig+31-3| ... | @@ -25842,6 +25842,9 @@ fn structFieldPtrByIndex( | ... | @@ -25842,6 +25842,9 @@ fn structFieldPtrByIndex( |
| 25842 | 25842 | ||
| 25843 | const target = mod.getTarget(); | 25843 | const target = mod.getTarget(); |
| 25844 | 25844 | ||
| 25845 | const parent_align = struct_ptr_ty_info.flags.alignment.toByteUnitsOptional() orelse | ||
| 25846 | try sema.typeAbiAlignment(struct_ptr_ty_info.child.toType()); | ||
| 25847 | |||
| 25845 | if (struct_obj.layout == .Packed) { | 25848 | if (struct_obj.layout == .Packed) { |
| 25846 | comptime assert(Type.packed_struct_layout_version == 2); | 25849 | comptime assert(Type.packed_struct_layout_version == 2); |
| 25847 | 25850 | ||
| ... | @@ -25863,8 +25866,6 @@ fn structFieldPtrByIndex( | ... | @@ -25863,8 +25866,6 @@ fn structFieldPtrByIndex( |
| 25863 | ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset; | 25866 | ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset; |
| 25864 | } | 25867 | } |
| 25865 | 25868 | ||
| 25866 | const parent_align = struct_ptr_ty_info.flags.alignment.toByteUnitsOptional() orelse | ||
| 25867 | struct_ptr_ty_info.child.toType().abiAlignment(mod); | ||
| 25868 | ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align); | 25869 | ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align); |
| 25869 | 25870 | ||
| 25870 | // If the field happens to be byte-aligned, simplify the pointer type. | 25871 | // If the field happens to be byte-aligned, simplify the pointer type. |
| ... | @@ -25888,8 +25889,13 @@ fn structFieldPtrByIndex( | ... | @@ -25888,8 +25889,13 @@ fn structFieldPtrByIndex( |
| 25888 | ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 }; | 25889 | ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 }; |
| 25889 | } | 25890 | } |
| 25890 | } | 25891 | } |
| 25892 | } else if (struct_obj.layout == .Extern and field_index == 0) { | ||
| 25893 | // This is the first field in memory, so can inherit the struct alignment | ||
| 25894 | ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align); | ||
| 25891 | } else { | 25895 | } else { |
| 25892 | ptr_ty_data.flags.alignment = field.abi_align; | 25896 | // Our alignment is capped at the field alignment |
| 25897 | const field_align = try sema.structFieldAlignment(field, struct_obj.layout); | ||
| 25898 | ptr_ty_data.flags.alignment = Alignment.fromByteUnits(@min(field_align, parent_align)); | ||
| 25893 | } | 25899 | } |
| 25894 | 25900 | ||
| 25895 | const ptr_field_ty = try mod.ptrType(ptr_ty_data); | 25901 | const ptr_field_ty = try mod.ptrType(ptr_ty_data); |
| ... | @@ -35952,6 +35958,28 @@ fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 { | ... | @@ -35952,6 +35958,28 @@ fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 { |
| 35952 | field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty))); | 35958 | field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty))); |
| 35953 | } | 35959 | } |
| 35954 | 35960 | ||
| 35961 | /// Keep implementation in sync with `Module.Struct.Field.alignment`. | ||
| 35962 | fn structFieldAlignment(sema: *Sema, field: Module.Struct.Field, layout: std.builtin.Type.ContainerLayout) !u32 { | ||
| 35963 | const mod = sema.mod; | ||
| 35964 | if (field.abi_align.toByteUnitsOptional()) |a| { | ||
| 35965 | assert(layout != .Packed); | ||
| 35966 | return @as(u32, @intCast(a)); | ||
| 35967 | } | ||
| 35968 | switch (layout) { | ||
| 35969 | .Packed => return 0, | ||
| 35970 | .Auto => if (mod.getTarget().ofmt != .c) { | ||
| 35971 | return sema.typeAbiAlignment(field.ty); | ||
| 35972 | }, | ||
| 35973 | .Extern => {}, | ||
| 35974 | } | ||
| 35975 | // extern | ||
| 35976 | const ty_abi_align = try sema.typeAbiAlignment(field.ty); | ||
| 35977 | if (field.ty.isAbiInt(mod) and field.ty.intInfo(mod).bits >= 128) { | ||
| 35978 | return @max(ty_abi_align, 16); | ||
| 35979 | } | ||
| 35980 | return ty_abi_align; | ||
| 35981 | } | ||
| 35982 | |||
| 35955 | /// Synchronize logic with `Type.isFnOrHasRuntimeBits`. | 35983 | /// Synchronize logic with `Type.isFnOrHasRuntimeBits`. |
| 35956 | pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { | 35984 | pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { |
| 35957 | const mod = sema.mod; | 35985 | const mod = sema.mod; |
test/behavior/struct.zig+77| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const native_endian = builtin.target.cpu.arch.endian(); | 3 | const native_endian = builtin.target.cpu.arch.endian(); |
| 4 | const assert = std.debug.assert; | ||
| 4 | const expect = std.testing.expect; | 5 | const expect = std.testing.expect; |
| 5 | const expectEqual = std.testing.expectEqual; | 6 | const expectEqual = std.testing.expectEqual; |
| 6 | const expectEqualSlices = std.testing.expectEqualSlices; | 7 | const expectEqualSlices = std.testing.expectEqualSlices; |
| ... | @@ -1637,3 +1638,79 @@ test "instantiate struct with comptime field" { | ... | @@ -1637,3 +1638,79 @@ test "instantiate struct with comptime field" { |
| 1637 | comptime std.debug.assert(things.foo == 1); | 1638 | comptime std.debug.assert(things.foo == 1); |
| 1638 | } | 1639 | } |
| 1639 | } | 1640 | } |
| 1641 | |||
| 1642 | test "struct field pointer has correct alignment" { | ||
| 1643 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1644 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1645 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1646 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 1647 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 1648 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 1649 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO | ||
| 1650 | |||
| 1651 | const S = struct { | ||
| 1652 | fn doTheTest() !void { | ||
| 1653 | var a: struct { x: u32 } = .{ .x = 123 }; | ||
| 1654 | var b: struct { x: u32 } align(1) = .{ .x = 456 }; | ||
| 1655 | var c: struct { x: u32 } align(64) = .{ .x = 789 }; | ||
| 1656 | |||
| 1657 | const ap = &a.x; | ||
| 1658 | const bp = &b.x; | ||
| 1659 | const cp = &c.x; | ||
| 1660 | |||
| 1661 | comptime assert(@TypeOf(ap) == *u32); | ||
| 1662 | comptime assert(@TypeOf(bp) == *align(1) u32); | ||
| 1663 | comptime assert(@TypeOf(cp) == *u32); // undefined layout, cannot inherit larger alignment | ||
| 1664 | |||
| 1665 | try expectEqual(@as(u32, 123), ap.*); | ||
| 1666 | try expectEqual(@as(u32, 456), bp.*); | ||
| 1667 | try expectEqual(@as(u32, 789), cp.*); | ||
| 1668 | } | ||
| 1669 | }; | ||
| 1670 | |||
| 1671 | try S.doTheTest(); | ||
| 1672 | try comptime S.doTheTest(); | ||
| 1673 | } | ||
| 1674 | |||
| 1675 | test "extern struct field pointer has correct alignment" { | ||
| 1676 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1677 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1678 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1679 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 1680 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 1681 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 1682 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO | ||
| 1683 | |||
| 1684 | const S = struct { | ||
| 1685 | fn doTheTest() !void { | ||
| 1686 | var a: extern struct { x: u32, y: u32 } = .{ .x = 1, .y = 2 }; | ||
| 1687 | var b: extern struct { x: u32, y: u32 } align(1) = .{ .x = 3, .y = 4 }; | ||
| 1688 | var c: extern struct { x: u32, y: u32 } align(64) = .{ .x = 5, .y = 6 }; | ||
| 1689 | |||
| 1690 | const axp = &a.x; | ||
| 1691 | const bxp = &b.x; | ||
| 1692 | const cxp = &c.x; | ||
| 1693 | const ayp = &a.y; | ||
| 1694 | const byp = &b.y; | ||
| 1695 | const cyp = &c.y; | ||
| 1696 | |||
| 1697 | comptime assert(@TypeOf(axp) == *u32); | ||
| 1698 | comptime assert(@TypeOf(bxp) == *align(1) u32); | ||
| 1699 | comptime assert(@TypeOf(cxp) == *align(64) u32); // first field, inherits larger alignment | ||
| 1700 | comptime assert(@TypeOf(ayp) == *u32); | ||
| 1701 | comptime assert(@TypeOf(byp) == *align(1) u32); | ||
| 1702 | comptime assert(@TypeOf(cyp) == *u32); | ||
| 1703 | |||
| 1704 | try expectEqual(@as(u32, 1), axp.*); | ||
| 1705 | try expectEqual(@as(u32, 3), bxp.*); | ||
| 1706 | try expectEqual(@as(u32, 5), cxp.*); | ||
| 1707 | |||
| 1708 | try expectEqual(@as(u32, 2), ayp.*); | ||
| 1709 | try expectEqual(@as(u32, 4), byp.*); | ||
| 1710 | try expectEqual(@as(u32, 6), cyp.*); | ||
| 1711 | } | ||
| 1712 | }; | ||
| 1713 | |||
| 1714 | try S.doTheTest(); | ||
| 1715 | try comptime S.doTheTest(); | ||
| 1716 | } |