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

@@ -935,7 +935,11 @@ resolve_type(Checker *c, Node *n)
if (!require_sized(c, ft, f->pos, "a struct field"))
continue;
if (ft->align > maxalign) maxalign = ft->align;
off = (off + ft->align - 1) & ~(ft->align - 1);
/* packed: no inter-field padding (harec
* type_store.c:206-213); align still tracks the
* max field align below. */
if (!n->packed)
off = (off + ft->align - 1) & ~(ft->align - 1);
if (f->str != NULL) {
/* regular named field */
for (Tfield *e = head; e; e = e->next)
@@ -985,8 +989,12 @@ resolve_type(Checker *c, Node *n)
off = base + inner->size;
}
t->fields = head;
t->packed = n->packed;
t->align = maxalign;
t->size = (off + maxalign - 1) & ~(maxalign - 1);
/* packed: skip the trailing pad-to-align (harec
* type_store.c:886 `!packed`); align value unchanged. */
t->size = n->packed ? off
: ((off + maxalign - 1) & ~(maxalign - 1));
return t;
}
case N_TENUM: {

View File

@@ -204,8 +204,25 @@ parsetype(Parser *p)
}
case TK_STRUCT: {
advance(p);
expect(p, TK_LBRACE);
Node *n = newnode(p->a, N_TSTRUCT, pp);
/* `@packed` is an inline struct TYPE attribute (harec
* ast.h:95 `bool packed`), sitting after `struct` and
* before `{` — NOT a fn-decl attr, so it does not route
* through parseattrs. */
if (p->cur.kind == TK_AT) {
advance(p);
const char *an = expectident(p);
/* p->errs gates the build (cmd/w6c/main.c:82);
* nerrors is advisory-only, so an errorf on a
* clean-recovery path must bump p->errs itself. */
if (strcmp(an, "packed") != 0) {
errorf(p->cur.pos,
"unknown struct attribute '@%s'", an);
p->errs++;
} else
n->packed = 1;
}
expect(p, TK_LBRACE);
Node *head = NULL, *tail = NULL;
while (p->cur.kind != TK_RBRACE && p->cur.kind != TK_EOF) {
Pos fp = p->cur.pos;

View File

@@ -267,6 +267,9 @@ type_eq(Type *a, Type *b)
return pa == NULL && pb == NULL;
}
case TY_STRUCT: {
/* packed is part of struct identity: a packed struct is
* not equal to its unpacked twin (harec types.c:621). */
if (a->packed != b->packed) return 0;
Tfield *fa = a->fields, *fb = b->fields;
while (fa && fb) {
if (strcmp(fa->name, fb->name) != 0) return 0;

View File

@@ -345,6 +345,8 @@ struct Node {
Node *next; /* sibling link inside `list` */
Node *attr; /* @attribute chain (N_ATTR list) */
int export;
int packed; /* N_TSTRUCT: `struct @packed` — no field/
* trailing padding (harec ast.h:95). */
Type *type; /* filled in by checker */
const char *tsuffix; /* typed numeric literal suffix */
const char *module; /* `// MODULE: foo` directive at the
@@ -487,6 +489,8 @@ struct Type {
* stored as a single 8-byte pointer; null
* is the void variant. Mirrors Hare's
* `(*T | null)` folding. */
int packed; /* TY_STRUCT laid out with no padding; part
* of type identity (harec types.c:517/621). */
};
extern Type *ty_void, *ty_bool, *ty_rune;