diff options
-rw-r--r-- | expr.c | 41 | ||||
-rw-r--r-- | test/builtin-offsetof.c | 9 | ||||
-rw-r--r-- | test/builtin-offsetof.qbe | 1 |
3 files changed, 50 insertions, 1 deletions
@@ -400,11 +400,48 @@ primaryexpr(struct scope *s) static struct expr *condexpr(struct scope *); +/* TODO: merge with init.c:designator() */ +static void +designator(struct scope *s, struct type *t, uint64_t *offset) +{ + char *name; + struct member *m; + uint64_t i; + + for (;;) { + switch (tok.kind) { + case TLBRACK: + if (t->kind != TYPEARRAY) + error(&tok.loc, "index designator is only valid for array types"); + next(); + i = intconstexpr(s, false); + expect(TRBRACK, "for index designator"); + t = t->base; + *offset += i * t->size; + break; + case TPERIOD: + if (t->kind != TYPESTRUCT && t->kind != TYPEUNION) + error(&tok.loc, "member designator only valid for struct/union types"); + next(); + name = expect(TIDENT, "for member designator"); + m = typemember(t, name, offset); + if (!m) + error(&tok.loc, "%s has no member named '%s'", t->kind == TYPEUNION ? "union" : "struct", name); + free(name); + t = m->type; + break; + default: + return; + } + } +} + static struct expr * builtinfunc(struct scope *s, enum builtinkind kind) { struct expr *e, *param; struct type *t; + struct member *m; char *name; uint64_t offset; @@ -437,8 +474,10 @@ builtinfunc(struct scope *s, enum builtinkind kind) if (t->kind != TYPESTRUCT && t->kind != TYPEUNION) error(&tok.loc, "type is not a struct/union type"); offset = 0; - if (!typemember(t, name, &offset)) + m = typemember(t, name, &offset); + if (!m) error(&tok.loc, "struct/union has no member named '%s'", name); + designator(s, m->type, &offset); e = mkconstexpr(&typeulong, offset); free(name); break; diff --git a/test/builtin-offsetof.c b/test/builtin-offsetof.c new file mode 100644 index 0000000..1737d0e --- /dev/null +++ b/test/builtin-offsetof.c @@ -0,0 +1,9 @@ +struct s { + struct { + union { + float z; + char *c; + } b; + } a[5]; +}; +int x = __builtin_offsetof(struct s, a[2].b.c); diff --git a/test/builtin-offsetof.qbe b/test/builtin-offsetof.qbe new file mode 100644 index 0000000..1efe7e7 --- /dev/null +++ b/test/builtin-offsetof.qbe @@ -0,0 +1 @@ +export data $x = align 4 { w 16, } |