parse,check,type,wwi: @packed struct attribute, both stages (#51)

Hare/harec @packed struct layout: no inter-field or trailing padding, align =
max field align (NOT forced to 1) — matches harec type_store.c + types.c:621
(packed{u8,u64}=size 9/align 8). Parser consumes inline @packed (loud-rejects
unknown struct attrs, both stages); layout gates padding on !packed; cstage
type_eq enforces packed type-distinctness; the .wwi producer round-trips
"struct @packed {". wwstage sets slotsize=size for packed so its composite-ABI
copy matches cstage byte-for-byte. cstage identity is faithful; wwstage identity
rides the deferred #224 nominal-resolvealias arc (#108). Both stages byte-id;
447 tests pass.
This commit is contained in:
2026-06-19 00:17:49 +09:00
parent 0197dfb9e6
commit 027f90c572
12 changed files with 563 additions and 16 deletions

View File

@@ -1311,11 +1311,17 @@ fn astsize(c: *checker, t: *syntax.node) i64 = {
if (f.kind == syntax.nkind.N_TFIELD) {
let fa: i64 = astalign(c, f.lhs);
if (fa > maxal) { maxal = fa; };
off = (off + fa - 1i64) & ~(fa - 1i64);
// packed: no inter-field padding (harec
// type_store.c:206-213); align value unchanged.
if (t.packed == 0) {
off = (off + fa - 1i64) & ~(fa - 1i64);
};
off += astsize(c, f.lhs);
};
f = f.next;
};
// packed: skip trailing pad-to-align (harec type_store.c:886).
if (t.packed != 0) { return off; };
return (off + maxal - 1i64) & ~(maxal - 1i64);
};
if (k == syntax.nkind.N_TTAGGED) {
@@ -1528,7 +1534,10 @@ fn astoffset(c: *checker, dot: *syntax.node) i64 = {
for (f != nil) {
if (f.kind == syntax.nkind.N_TFIELD) {
let fa: i64 = astalign(c, f.lhs);
off = (off + fa - 1i64) & ~(fa - 1i64);
// packed: no inter-field padding (harec type_store.c:206-213).
if (rtyp.packed == 0) {
off = (off + fa - 1i64) & ~(fa - 1i64);
};
if (syntax.streq(f.str, dot.str)) { return off; };
off += astsize(c, f.lhs);
};
@@ -2279,7 +2288,10 @@ fn tinfofornode(c: *checker, n: *syntax.node) *syntax.tinfo = {
if (circularnamed(c, ft, f)) { ft = c.tc.tyerr; };
if (ft != nil) {
if (ft.align > maxalign) { maxalign = ft.align; };
if (ft.align > 0u64) {
// packed: no inter-field padding (harec
// type_store.c:206-213); align still tracks the
// max field align below.
if (n.packed == 0 && ft.align > 0u64) {
off = (off + ft.align - 1u64) & ~(ft.align - 1u64);
};
let fldoff: u64 = off;
@@ -2303,14 +2315,29 @@ fn tinfofornode(c: *checker, n: *syntax.node) *syntax.tinfo = {
f = f.next;
};
r.fields = fh;
if (maxalign > 0u64) {
r.size = (off + maxalign - 1u64) & ~(maxalign - 1u64);
r.packed = n.packed;
// packed: skip the trailing pad-to-align (harec
// type_store.c:886 `!packed`); align value unchanged.
if (n.packed != 0) {
r.size = off;
} else {
if (maxalign > 0u64) {
r.size = (off + maxalign - 1u64) & ~(maxalign - 1u64);
};
};
r.align = maxalign;
if ((soff & 7u64) != 0u64) {
soff = (soff + 7u64) & ~7u64;
// packed: slotsize == size — cstage has no slotsize, it uses the
// (packed) size everywhere incl struct-ABI copy (cmd/w6c/cgen.c:
// 15020). Aligning slotsize DOWN to size keeps every wwstage
// slot-padded consumer byte-identical to cstage (rule 10).
if (n.packed != 0) {
r.slotsize = r.size;
} else {
if ((soff & 7u64) != 0u64) {
soff = (soff + 7u64) & ~7u64;
};
r.slotsize = soff;
};
r.slotsize = soff;
case syntax.nkind.N_TTAGGED:
// THE tagged-union normalization SSoT (astsize/astalign delegate
// here). Mirrors cstage resolve_type N_TTAGGED (cmd/wcc/check.c:

View File

@@ -361,7 +361,13 @@ fn wwitype(fd: i32, t: *syntax.node) void = {
wputs(fd, ") ");
wwitype(fd, t.lhs);
} else { if (t.kind == syntax.nkind.N_TSTRUCT) {
wputs(fd, "struct { ");
// re-emit `@packed` so the flag round-trips through
// sep-compile (harec unparse/type.ha:122-126).
if (t.packed != 0) {
wputs(fd, "struct @packed { ");
} else {
wputs(fd, "struct { ");
};
let f: *syntax.node = t.list;
for (f != nil) {
if (f != t.list) { wputs(fd, ", "); };