From 1c04435e5d33378ffa8eca65ca1ed35f3f9f4134 Mon Sep 17 00:00:00 2001 From: lemon Date: Mon, 29 May 2023 11:37:32 +0200 Subject: field access --- common.h | 34 ++++++++++++------ parse.c | 122 +++++++++++++++++++++++++++++++++++++++++++++------------------ parse.h | 15 ++++---- test.c | 26 +++++++++++++- type.c | 40 +++++++++++++++++---- 5 files changed, 179 insertions(+), 58 deletions(-) diff --git a/common.h b/common.h index 2bd42bc..872fdd6 100644 --- a/common.h +++ b/common.h @@ -162,11 +162,16 @@ struct enumvar { union { vlong i; uvlong u; }; }; -struct field { - const char *name; +struct fielddata { union type t; ushort off; - uchar bitsiz, bitoff; + uchar bitsiz, + bitoff : 6, + qual : 2; +}; +struct namedfield { + const char *name; + struct fielddata f; }; struct typedata { @@ -174,14 +179,22 @@ struct typedata { ushort id; union { union type child; + struct { /* functions */ + const uchar *quals; /* packed N x 2bit array (NULL if no param has quals) */ + const union type *param; + }; struct { /* aggregates */ - const uchar *quals; /* packed N x 2bit array (NULL if no member has quals) */ - union { - struct field *fld; - const union type *param; - }; + /* struct fieldmap { + union { + struct field *fs; + int *is; + }; + const char **k; + uint ; + } *fmap; */ + struct namedfield *fld; }; - struct { + struct { /* enum */ uchar backing; struct enumvar *var; }; @@ -189,7 +202,7 @@ struct typedata { union { uint arrlen; /* array */ struct { - short nmemb; + short nmemb; /* functions, aggregates, enums */ uchar align; union { struct { /* function */ @@ -231,6 +244,7 @@ union type mkptrtype(union type, int qual); union type mkarrtype(union type t, int qual, uint n); union type mkfntype(union type ret, uint n, const union type *, const uchar *qual, bool kandr, bool variadic); union type mktagtype(const char *name, struct typedata *td); +bool getfield(struct fielddata *res, union type, const char *); union type completetype(const char *name, int id, struct typedata *td); union type typedecay(union type); bool assigncompat(union type dst, union type src); diff --git a/parse.c b/parse.c index 3da90c0..ceef1f2 100644 --- a/parse.c +++ b/parse.c @@ -219,6 +219,7 @@ finddecl(struct parser *pr, const char *name) { struct env *e; struct decl *l; + assert(name); for (e = pr->env; e; e = e->up) { for (l = NULL; enviterdecl(&l, e);) { if (name == l->name) @@ -234,6 +235,7 @@ gettagged(struct parser *pr, struct span *span, enum typetag tt, const char *nam struct env *e; struct tagged *l; struct typedata td = {0}; + assert(name); for (e = pr->env; e; e = e->up) { for (l = NULL; envitertagged(&l, e);) { if (name == ttypenames[typedata[l->ty.dat].id]) { @@ -256,6 +258,7 @@ deftagged(struct parser *pr, struct span *span, enum typetag tt, const char *nam { struct tagged *l; struct typedata td = {0}; + assert(name); for (l = NULL; envitertagged(&l, pr->env);) { if (name == ttypenames[typedata[l->ty.dat].id]) { *span = l->span; @@ -473,6 +476,8 @@ bintypecheck(const struct span *span, enum toktag tt, struct expr *lhs, struct e } switch (k &~ BCSET) { case 0: + if (isagg(ty) && !(lhs->qual & QCONST) && typedata[ty.dat].anyconst) + error(&lhs->span, "cannot assign to aggregate with const-qualified member"); if (!assigncheck(ty, rhs)) goto Error; break; @@ -639,7 +644,7 @@ tkprec(int tt) static struct expr exprparse(struct parser *pr, int prec) { - struct token tk; + struct token tk, tk2; struct span span; struct expr ex, rhs, tmp; struct decl *decl; @@ -683,7 +688,7 @@ Unary: break; case TKSTRLIT: ex = mkexpr(ESTRLIT, tk.span, - mkarrtype(mktype(TYCHAR), 0, tk.len+1), .s = { (char *)tk.s, tk.len }); + mkarrtype(mktype(TYCHAR), 0, tk.len+1), .s = { (uchar *)tk.s, tk.len }); break; case TKIDENT: decl = finddecl(pr, tk.s); @@ -754,7 +759,7 @@ Postfix: rhs = commaexpr(pr); span = ex.span; if (!joinspan(&span.ex, tk.span.ex) || !joinspan(&span.ex, ex.span.ex) - || (peek(pr, &tk), !joinspan(&span.ex, tk.span.ex))) + || (peek(pr, &tk2), !joinspan(&span.ex, tk.span.ex))) span = tk.span; expect(pr, ']', NULL); @@ -776,14 +781,48 @@ Postfix: tmp.sub = exprdup(pr, iszero(rhs) ? &ex : &tmp); tmp.span = span; tmp.t = EDEREF; + tmp.qual = ex.ty.flag & TFCHLDQUAL; tmp.ty = ty; ex = tmp; goto Postfix; case '(': /* call(args) */ - span = ex.span; lex(pr, &tk); + span = ex.span; ex = callexpr(pr, &span, &ex); goto Postfix; + case TKARROW: + if (ex.ty.t != TYPTR && ex.ty.t != TYARRAY) + error(&ex.span, "operand to -> is not a pointer (%ty)", ex.ty); + else + ex = mkexpr(EDEREF, ex.span, typechild(ex.ty), .qual = ex.ty.flag & TFCHLDQUAL, + .sub = exprdup(pr, &ex)); + /* fallthru */ + case '.': + lex(pr, &tk); + span = ex.span; + peek(pr, &tk2); /* field name */ + if (!expect(pr, TKIDENT, NULL)) tk2.s = ""; + if (!joinspan(&span.ex, tk.span.ex) || !joinspan(&span.ex, tk2.span.ex)) + span = tk.span; + if (!isagg(ex.ty)) { + error(&span, "member access operand is not an aggregate (%ty)%s", ex.ty, + ex.ty.t == TYPTR && isagg(typechild(ex.ty)) ? "; did you mean to use '->'?" : ""); + } else { + struct fielddata fld = {.t = mktype(TYINT)}; + if (*tk2.s && !getfield(&fld, ex.ty, tk2.s)) + error(&span, "'%ty' has no such field: '%s'", ex.ty, tk2.s); + if (ex.t == EGETF && ex.qual == fld.qual) { /* accumulate */ + ex.span = span; + ex.ty = fld.t; + ex.fld.off += fld.off; + ex.fld.bitoff = fld.bitoff; + ex.fld.bitsiz = fld.bitsiz; + } else { + ex = mkexpr(EGETF, span, fld.t, .qual = ex.qual | fld.qual, .sub = exprdup(pr, &ex), + .fld = { fld.off, fld.bitsiz, fld.bitoff }); + } + } + goto Postfix; } /* unary operators (process) */ @@ -914,6 +953,8 @@ static union irref expraddr(struct function *fn, const struct expr *ex) { struct decl *decl; + union irref r; + struct instr ins; switch (ex->t) { case ESYM: @@ -933,6 +974,16 @@ expraddr(struct function *fn, const struct expr *ex) break; case EDEREF: return exprvalue(fn, ex->sub); + case EGETF: + r = expraddr(fn, ex->sub); + assert(ex->fld.bitsiz == 0); + if (ex->fld.off == 0) return r; + ins.cls = KPTR; + ins.op = Oadd; + ins.l = r; + ins.r = mkintcon(fn, KI4, ex->fld.off); + return addinstr(fn, ins); + break; default: assert(!"lvalue?>"); } @@ -1071,7 +1122,10 @@ exprvalue(struct function *fn, const struct expr *ex) enum irclass cls = type2cls[ex->ty.t]; struct instr ins = {0}; int swp = 0; - struct expr *sub = ex->sub; + struct expr *sub; + + eval((struct expr *)ex, EVFOLD); + sub = ex->sub; if (ex->ty.t == TYARRAY || ex->ty.t == TYFUNC) return expraddr(fn, ex); @@ -1082,6 +1136,8 @@ exprvalue(struct function *fn, const struct expr *ex) return mkintcon(fn, cls, ex->i); case ESYM: return genload(fn, ex->ty, expraddr(fn, ex)); + case EGETF: + return genload(fn, ex->ty, expraddr(fn, ex)); case ECAST: if (ex->ty.t == TYVOID) return exprvalue(fn, sub); case EPLUS: @@ -1329,8 +1385,6 @@ stmt(struct parser *pr, struct function *fn) t = newblk(fn); f = newblk(fn); r = exprvalue(fn, &ex); - if (!isint(ex.ty)) - r = cvt(fn, TYINT, ex.ty.t, r); EMITS { putjump(fn, Jbcnd, r, t, f); useblk(fn, t); @@ -1366,8 +1420,6 @@ stmt(struct parser *pr, struct function *fn) putjump(fn, Jb, NOREF, begin, NULL); useblk(fn, begin); r = exprvalue(fn, &ex); - if (!isint(ex.ty)) - r = cvt(fn, TYINT, ex.ty.t, r); EMITS { putjump(fn, Jbcnd, r, t = newblk(fn), end = newblk(fn)); useblk(fn, t); @@ -1535,10 +1587,8 @@ buildagg(struct parser *pr, enum typetag tt, const char *name, int id) struct token tk; union type t; struct span flexspan; - struct field fbuf[32]; - uchar qbuf[arraylength(fbuf)/4]; - vec_of(struct field) fld = VINIT(fbuf, arraylength(fbuf)); - vec_of(uchar) qual = VINIT(qbuf, arraylength(qbuf)); + struct namedfield fbuf[32]; + vec_of(struct namedfield) fld = VINIT(fbuf, arraylength(fbuf)); struct typedata td = {tt}; bool isunion = tt == TYUNION; const char *tag = isunion ? "union" : "struct"; @@ -1563,12 +1613,13 @@ buildagg(struct parser *pr, enum typetag tt, const char *name, int id) uint align = typealign(decl.ty); uint siz = typesize(decl.ty); uint off = isunion ? 0 : alignup(td.siz, align); - struct field f = { decl.name, decl.ty, off }; + struct namedfield f = { decl.name, { decl.ty, off, .qual = decl.qual }}; vpush(&fld, f); - if (decl.qual) { - td.anyconst |= decl.qual & QCONST; - while (qual.n < tdqualsiz(fld.n)) vpush(&qual, 0); - tdsetqual(qual.p, fld.n-1, decl.qual); + td.anyconst |= decl.qual & QCONST; + if (isagg(decl.ty)) { + td.anyconst |= typedata[decl.ty.dat].anyconst; + if (typedata[decl.ty.dat].flexi) + error(&decl.span, "nested aggregate has flexible array member"); } if (isunion) td.siz = td.siz < siz ? siz : td.siz; @@ -1579,19 +1630,19 @@ buildagg(struct parser *pr, enum typetag tt, const char *name, int id) } while (st.more); } if (fld.n == 0) { - struct field dummy = { "", mktype(TYCHAR), 0 }; + struct namedfield dummy = { "", { mktype(TYCHAR), 0 }}; error(&tk.span, "%s cannot have zero members", tag); vpush(&fld, dummy); td.siz = td.align = 1; } td.siz = alignup(td.siz, td.align); - if (qual.p) while (qual.n < tdqualsiz(fld.n)) vpush(&qual, 0); - td.quals = qual.p; td.fld = fld.p; td.nmemb = fld.n; - t = completetype(name, id, &td); + if (id != -1) + t = completetype(name, id, &td); + else + t = mktagtype(name, &td); vfree(&fld); - vfree(&qual); return t; } @@ -1616,6 +1667,7 @@ tagtype(struct parser *pr, enum toktag kind) enum typetag tt = kind == TKWenum ? TYENUM : kind == TKWstruct ? TYSTRUCT : TYUNION; const char *tag = NULL; + peek(pr, &tk); if (match(pr, &tk, TKIDENT)) tag = tk.s; span = tk.span; @@ -1627,17 +1679,19 @@ tagtype(struct parser *pr, enum toktag kind) t = gettagged(pr, &span, tt, tag, /* def? */ peek(pr, NULL) == ';'); } else { if (tt != TYENUM) { - t = deftagged(pr, &span, tt, tag); - if (t.t != tt || !isincomplete(t)) { - if (t.t != tt) - error(&tk.span, - "defining tagged type %'tk as %tt clashes with previous definition", - &tk, kind); - else - error(&tk.span, "redefinition of '%tt %s'", kind, tag); - note(&span, "previous definition:"); + if (tag) { + t = deftagged(pr, &span, tt, tag); + if (t.t != tt || !isincomplete(t)) { + if (t.t != tt) + error(&tk.span, + "defining tagged type %'tk as %tt clashes with previous definition", + &tk, kind); + else + error(&tk.span, "redefinition of '%tt %s'", kind, tag); + note(&span, "previous definition:"); + } } - t = buildagg(pr, tt, tag, typedata[t.dat].id); + t = buildagg(pr, tt, tag, tag ? typedata[t.dat].id : -1); } else { t = buildenum(pr, tag); } @@ -2111,7 +2165,7 @@ pdecl(struct declstate *st, struct parser *pr) { } if (first && st->tagdecl && match(pr, &tk, ';')) { - decl = (struct decl) { st->base, st->scls, st->qual, st->align }; + decl = (struct decl) { st->base, st->scls, st->qual, st->align, tk.span }; return decl; } decl = declarator(st, pr); diff --git a/parse.h b/parse.h index dee1115..a28c301 100644 --- a/parse.h +++ b/parse.h @@ -152,17 +152,17 @@ struct expr { union type ty; struct span span; union { - struct expr *sub; + struct { + struct expr *sub; + struct { + ushort off; + uchar bitsiz, bitoff; + } fld; /* EGETF */ + }; uvlong u; vlong i; double f; /* ENUMLIT */ struct bytes s; /* ESTRLIT */ struct decl *sym; /* ESYM */ struct initializer *ini; /* EINIT */ - struct { /* EGETF */ - const char *name; - uint off; - uchar bitsiz, bitoff; - } fld; - char dummy; }; }; @@ -180,6 +180,7 @@ enum evalmode { EVINTCONST, EVARITH, EVSTATICINI, + EVFOLD, }; bool eval(struct expr *, enum evalmode); diff --git a/test.c b/test.c index 1d6c01f..9a3aafb 100644 --- a/test.c +++ b/test.c @@ -13,7 +13,31 @@ int add (int x, int y) { return x + y + *glob; } -struct foo {struct foo *foo;}; +struct foo { + struct foo *foo; + struct n0 { + char z; + struct n1 { + char q; + struct n2 { + int ww; + } n2; + } n1; + } n0; + union { + struct { short x, y; }; + int xy; + }; + char flex[]; +}; + +int foop(struct foo *foo) { + if (foo->foo) foo = foo->foo; + ++foo->n0.n1.n2.ww; + int xy = (*foo).xy; + foo->flex[2] *= 5; + if (foo->x)return xy; else return foo->y; +} int abs(int x){ return (x ^ x >> 3\ diff --git a/type.c b/type.c index 5f984d6..66e5e63 100644 --- a/type.c +++ b/type.c @@ -98,15 +98,12 @@ interntd(const struct typedata *td) case TYUNION: if (slot->fld) slot->fld = alloccopy(&datarena, td->fld, nmemb * sizeof *slot->fld, 0); - Qual: - if (slot->quals) - slot->quals = alloccopy(&datarena, td->quals, tdqualsiz(nmemb), 1); break; case TYFUNC: - if (slot->param) { + if (slot->param) slot->param = alloccopy(&datarena, td->param, nmemb * sizeof *slot->param, 0); - } - goto Qual; + if (slot->quals) + slot->quals = alloccopy(&datarena, td->quals, tdqualsiz(nmemb), 1); } return i; } else if (tdequ(slot, td)) { @@ -214,6 +211,37 @@ mktagtype(const char *name, struct typedata *td) return completetype(name, id++, td); } +static bool +getfieldrec(struct fielddata *res, uint off, const struct typedata *td, const char *name) +{ +Begin: + for (int i = 0; i < td->nmemb; ++i) { + struct namedfield *fld = &td->fld[i]; + if (fld->name == name) { /* match */ + *res = fld->f; + res->off += off; + return 1; + } else if (!fld->name) { /* anonymous struct/union */ + const struct typedata *ftd = &typedata[fld->f.t.dat]; + assert(isagg(fld->f.t)); + if (i == td->nmemb - 1) { /* last field, tail recurse */ + off += fld->f.off; + td = ftd; + goto Begin; + } else if (getfieldrec(res, off + fld->f.off, ftd, name)) + return 1; + } + } + return 0; +} + +bool +getfield(struct fielddata *res, union type ty, const char *name) +{ + assert(isagg(ty)); + return getfieldrec(res, 0, &typedata[ty.dat], name); +} + union type typedecay(union type t) { -- cgit v1.2.3