authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-06-10 01:23:17+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-06-25 14:05:18+01:00
log2611d97fb07e5cba5657f508e93f01d60a2379bd
treefb2259414179756f86f79d176283d621a63b3168
parent3f04231600e7ec2e16f5fb33f2e81eea24bdc41b
signaturelock-open Commit is signed but in an unrecognized format.

Sema: copy pointer alignment to union field pointers

This implements the semantics as discussed in today's compiler meeting, where the alignment of pointers to fields of default-layout unions cannot exceed the field's alignment. Resolves: #15878

2 files changed, 120 insertions(+), 4 deletions(-)

src/Sema.zig+11-4
...@@ -26060,6 +26060,7 @@ fn unionFieldPtr(...@@ -26060,6 +26060,7 @@ fn unionFieldPtr(
26060 assert(unresolved_union_ty.zigTypeTag(mod) == .Union);26060 assert(unresolved_union_ty.zigTypeTag(mod) == .Union);
2606126061
26062 const union_ptr_ty = sema.typeOf(union_ptr);26062 const union_ptr_ty = sema.typeOf(union_ptr);
26063 const union_ptr_info = union_ptr_ty.ptrInfo(mod);
26063 const union_ty = try sema.resolveTypeFields(unresolved_union_ty);26064 const union_ty = try sema.resolveTypeFields(unresolved_union_ty);
26064 const union_obj = mod.typeToUnion(union_ty).?;26065 const union_obj = mod.typeToUnion(union_ty).?;
26065 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);26066 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
...@@ -26067,10 +26068,16 @@ fn unionFieldPtr(...@@ -26067,10 +26068,16 @@ fn unionFieldPtr(
26067 const ptr_field_ty = try mod.ptrType(.{26068 const ptr_field_ty = try mod.ptrType(.{
26068 .child = field.ty.toIntern(),26069 .child = field.ty.toIntern(),
26069 .flags = .{26070 .flags = .{
26070 .is_const = !union_ptr_ty.ptrIsMutable(mod),26071 .is_const = union_ptr_info.flags.is_const,
26071 .is_volatile = union_ptr_ty.isVolatilePtr(mod),26072 .is_volatile = union_ptr_info.flags.is_volatile,
26072 .address_space = union_ptr_ty.ptrAddressSpace(mod),26073 .address_space = union_ptr_info.flags.address_space,
26073 },26074 .alignment = if (union_obj.layout == .Auto) blk: {
26075 const union_align = union_ptr_info.flags.alignment.toByteUnitsOptional() orelse try sema.typeAbiAlignment(union_ty);
26076 const field_align = try sema.unionFieldAlignment(field);
26077 break :blk InternPool.Alignment.fromByteUnits(@min(union_align, field_align));
26078 } else union_ptr_info.flags.alignment,
26079 },
26080 .packed_offset = union_ptr_info.packed_offset,
26074 });26081 });
26075 const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?));26082 const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?));
2607626083
test/behavior/union.zig+109
...@@ -1583,3 +1583,112 @@ test "coerce enum literal to union in result loc" {...@@ -1583,3 +1583,112 @@ test "coerce enum literal to union in result loc" {
1583 try U.doTest(true);1583 try U.doTest(true);
1584 try comptime U.doTest(true);1584 try comptime U.doTest(true);
1585}1585}
1586
1587test "defined-layout union field pointer has correct alignment" {
1588 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1589 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1590 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1591 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1592 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1593 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1594 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1595
1596 const S = struct {
1597 fn doTheTest(comptime U: type) !void {
1598 var a: U = .{ .x = 123 };
1599 var b: U align(1) = .{ .x = 456 };
1600 var c: U align(64) = .{ .x = 789 };
1601
1602 const ap = &a.x;
1603 const bp = &b.x;
1604 const cp = &c.x;
1605
1606 comptime assert(@TypeOf(ap) == *u32);
1607 comptime assert(@TypeOf(bp) == *align(1) u32);
1608 comptime assert(@TypeOf(cp) == *align(64) u32);
1609
1610 try expectEqual(@as(u32, 123), ap.*);
1611 try expectEqual(@as(u32, 456), bp.*);
1612 try expectEqual(@as(u32, 789), cp.*);
1613 }
1614 };
1615
1616 const U1 = extern union { x: u32 };
1617 const U2 = packed union { x: u32 };
1618
1619 try S.doTheTest(U1);
1620 try S.doTheTest(U2);
1621 try comptime S.doTheTest(U1);
1622 try comptime S.doTheTest(U2);
1623}
1624
1625test "undefined-layout union field pointer has correct alignment" {
1626 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1627 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1628 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1629 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1630 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1631 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1632 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1633
1634 const S = struct {
1635 fn doTheTest(comptime U: type) !void {
1636 var a: U = .{ .x = 123 };
1637 var b: U align(1) = .{ .x = 456 };
1638 var c: U align(64) = .{ .x = 789 };
1639
1640 const ap = &a.x;
1641 const bp = &b.x;
1642 const cp = &c.x;
1643
1644 comptime assert(@TypeOf(ap) == *u32);
1645 comptime assert(@TypeOf(bp) == *align(1) u32);
1646 comptime assert(@TypeOf(cp) == *u32); // undefined layout so does not inherit larger aligns
1647
1648 try expectEqual(@as(u32, 123), ap.*);
1649 try expectEqual(@as(u32, 456), bp.*);
1650 try expectEqual(@as(u32, 789), cp.*);
1651 }
1652 };
1653
1654 const U1 = union { x: u32 };
1655 const U2 = union(enum) { x: u32 };
1656
1657 try S.doTheTest(U1);
1658 try S.doTheTest(U2);
1659 try comptime S.doTheTest(U1);
1660 try comptime S.doTheTest(U2);
1661}
1662
1663test "packed union field pointer has correct alignment" {
1664 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1665 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1666 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1667 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1668 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1669 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1670 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1671
1672 const U = packed union { x: u20 };
1673 const S = packed struct(u24) { a: u2, u: U, b: u2 };
1674
1675 var a: S = undefined;
1676 var b: S align(1) = undefined;
1677 var c: S align(64) = undefined;
1678
1679 const ap = &a.u.x;
1680 const bp = &b.u.x;
1681 const cp = &c.u.x;
1682
1683 comptime assert(@TypeOf(ap) == *align(4:2:3) u20);
1684 comptime assert(@TypeOf(bp) == *align(1:2:3) u20);
1685 comptime assert(@TypeOf(cp) == *align(64:2:3) u20);
1686
1687 a.u = .{ .x = 123 };
1688 b.u = .{ .x = 456 };
1689 c.u = .{ .x = 789 };
1690
1691 try expectEqual(@as(u20, 123), ap.*);
1692 try expectEqual(@as(u20, 456), bp.*);
1693 try expectEqual(@as(u20, 789), cp.*);
1694}