aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--expr.c41
-rw-r--r--test/builtin-offsetof.c9
-rw-r--r--test/builtin-offsetof.qbe1
3 files changed, 50 insertions, 1 deletions
diff --git a/expr.c b/expr.c
index 5c3e688..1ed040c 100644
--- a/expr.c
+++ b/expr.c
@@ -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, }