wcc: Hare-style struct embedding (anon + bare-name)
Struct members can be `struct { ... }` (anonymous nested) or a bare
named type, in addition to `name: type`. The inner struct's fields
are promoted to the outer scope with offsets shifted by the embed
base; codegen already keys off Tfield.offset so cgen needs no change.
Errors on non-struct embed or name collision.
This commit is contained in:
@@ -167,16 +167,56 @@ resolve_type(Checker *c, Node *n)
|
||||
Tfield *head = NULL, *tail = NULL;
|
||||
u64 off = 0, maxalign = 1;
|
||||
for (Node *f = n->list; f; f = f->next) {
|
||||
Tfield *tf = amalloc(c->a, sizeof *tf);
|
||||
tf->name = f->str;
|
||||
tf->type = resolve_type(c, f->lhs);
|
||||
if (tf->type->align > maxalign) maxalign = tf->type->align;
|
||||
off = (off + tf->type->align - 1) & ~(tf->type->align - 1);
|
||||
tf->offset = off;
|
||||
off += tf->type->size;
|
||||
if (head == NULL) head = tf;
|
||||
else tail->next = tf;
|
||||
tail = tf;
|
||||
Type *ft = resolve_type(c, f->lhs);
|
||||
if (ft->align > maxalign) maxalign = ft->align;
|
||||
off = (off + ft->align - 1) & ~(ft->align - 1);
|
||||
if (f->str != NULL) {
|
||||
/* regular named field */
|
||||
for (Tfield *e = head; e; e = e->next)
|
||||
if (e->name && strcmp(e->name, f->str) == 0) {
|
||||
err(c, f->pos, "duplicate field '%s'",
|
||||
f->str);
|
||||
break;
|
||||
}
|
||||
Tfield *tf = amalloc(c->a, sizeof *tf);
|
||||
tf->name = f->str;
|
||||
tf->type = ft;
|
||||
tf->offset = off;
|
||||
off += ft->size;
|
||||
if (head == NULL) head = tf;
|
||||
else tail->next = tf;
|
||||
tail = tf;
|
||||
continue;
|
||||
}
|
||||
/* embed (anonymous struct or bare-name): the inner type
|
||||
* must be a struct; its fields are promoted to the outer
|
||||
* scope with offsets shifted by the embed base. */
|
||||
Type *inner = (ft && ft->kind == TY_NAMED) ? ft->under : ft;
|
||||
if (inner == NULL || inner->kind != TY_STRUCT) {
|
||||
err(c, f->pos, "embedded type must be a struct");
|
||||
off += ft ? ft->size : 0;
|
||||
continue;
|
||||
}
|
||||
u64 base = off;
|
||||
for (Tfield *src = inner->fields; src; src = src->next) {
|
||||
for (Tfield *e = head; e; e = e->next)
|
||||
if (e->name && src->name &&
|
||||
strcmp(e->name, src->name) == 0) {
|
||||
err(c, f->pos,
|
||||
"embedded field '%s' "
|
||||
"collides with existing field",
|
||||
src->name);
|
||||
break;
|
||||
}
|
||||
Tfield *tf = amalloc(c->a, sizeof *tf);
|
||||
tf->name = src->name;
|
||||
tf->type = src->type;
|
||||
tf->offset = base + src->offset;
|
||||
if (head == NULL) head = tf;
|
||||
else tail->next = tf;
|
||||
tail = tf;
|
||||
}
|
||||
off = base + inner->size;
|
||||
}
|
||||
t->fields = head;
|
||||
t->align = maxalign;
|
||||
|
||||
@@ -191,9 +191,21 @@ parsetype(Parser *p)
|
||||
while (p->cur.kind != TK_RBRACE && p->cur.kind != TK_EOF) {
|
||||
Pos fp = p->cur.pos;
|
||||
Node *f = newnode(p->a, N_TFIELD, fp);
|
||||
f->str = expectident(p);
|
||||
expect(p, TK_COLON);
|
||||
f->lhs = parsetype(p);
|
||||
/* Three member forms:
|
||||
* name: type — regular field
|
||||
* struct { ... } — anonymous embedded struct
|
||||
* Identifier — bare-name embedded type
|
||||
* Embeds carry f->str == NULL and the type in f->lhs. */
|
||||
if (p->cur.kind == TK_STRUCT) {
|
||||
f->lhs = parsetype(p);
|
||||
} else if (p->cur.kind == TK_IDENT
|
||||
&& peek(p).kind != TK_COLON) {
|
||||
f->lhs = parsetype(p);
|
||||
} else {
|
||||
f->str = expectident(p);
|
||||
expect(p, TK_COLON);
|
||||
f->lhs = parsetype(p);
|
||||
}
|
||||
if (head == NULL) head = f;
|
||||
else tail->next = f;
|
||||
tail = f;
|
||||
|
||||
@@ -344,6 +344,20 @@ static const struct row rows[] = {
|
||||
" os.assert(r == 42, \"sum mismatch\\n\");\n"
|
||||
" return r: i32;\n"
|
||||
"};", 42 },
|
||||
/* Hare-style struct embedding: bare-name embed + anonymous
|
||||
* nested struct, fields promoted to the outer scope. Both
|
||||
* field-by-field assignment and flat struct-literal init must
|
||||
* see promoted fields. */
|
||||
{ "type point = struct { x: i32, y: i32 };\n"
|
||||
"type vec = struct {\n"
|
||||
" point,\n"
|
||||
" struct { z: i32 },\n"
|
||||
" w: i32,\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let v: vec = vec { x = 1, y = 2, z = 3, w = 36 };\n"
|
||||
" return v.x + v.y + v.z + v.w;\n"
|
||||
"};", 42 },
|
||||
/* 3-field tuple destructure in for-range */
|
||||
{ "fn main() i32 = {\n"
|
||||
" let buf: [3]i64;\n"
|
||||
|
||||
Reference in New Issue
Block a user