#include #include #include #include #include #include #include #include #include "util.h" #include "decl.h" #include "emit.h" #include "expr.h" #include "htab.h" #include "init.h" #include "pp.h" #include "scope.h" #include "stmt.h" #include "token.h" #include "type.h" struct declaration builtinvastart = {.kind = DECLBUILTIN}; struct declaration builtinvaend = {.kind = DECLBUILTIN}; struct declaration builtinoffsetof = {.kind = DECLBUILTIN}; static struct list tentativedefns = {&tentativedefns, &tentativedefns}; enum storageclass { SCNONE, SCTYPEDEF = 1<<1, SCEXTERN = 1<<2, SCSTATIC = 1<<3, SCAUTO = 1<<4, SCREGISTER = 1<<5, SCTHREADLOCAL = 1<<6, }; enum typespecifier { SPECNONE, SPECVOID = 1<<1, SPECCHAR = 1<<2, SPECBOOL = 1<<3, SPECINT = 1<<4, SPECFLOAT = 1<<5, SPECDOUBLE = 1<<6, SPECSHORT = 1<<7, SPECLONG = 1<<8, SPECLONG2 = 1<<9, SPECSIGNED = 1<<10, SPECUNSIGNED = 1<<11, SPECCOMPLEX = 1<<12, SPECLONGLONG = SPECLONG|SPECLONG2, }; enum funcspecifier { FUNCNONE, FUNCINLINE = 1<<1, FUNCNORETURN = 1<<2, }; struct declaration * mkdecl(enum declarationkind k, struct type *t, enum linkage linkage) { struct declaration *d; d = xmalloc(sizeof(*d)); d->kind = k; d->linkage = linkage; d->type = t; d->tentative = false; d->defined = false; d->align = 0; d->value = NULL; return d; } /* 6.7.1 Storage-class specifiers */ static int storageclass(enum storageclass *sc) { enum storageclass allowed, new; switch (tok.kind) { case TTYPEDEF: new = SCTYPEDEF; break; case TEXTERN: new = SCEXTERN; break; case TSTATIC: new = SCSTATIC; break; case T_THREAD_LOCAL: new = SCTHREADLOCAL; break; case TAUTO: new = SCAUTO; break; case TREGISTER: new = SCREGISTER; break; default: return 0; } if (!sc) error(&tok.loc, "storage class not allowed in this declaration"); switch (*sc) { case SCNONE: allowed = ~SCNONE; break; case SCTHREADLOCAL: allowed = SCSTATIC|SCEXTERN; break; case SCSTATIC: case SCEXTERN: allowed = SCTHREADLOCAL; break; default: allowed = SCNONE; break; } if (new & ~allowed) error(&tok.loc, "invalid combination of storage class specifiers"); *sc |= new; next(); return 1; } /* 6.7.3 Type qualifiers */ static int typequal(enum typequalifier *tq) { switch (tok.kind) { case TCONST: *tq |= QUALCONST; break; case TVOLATILE: *tq |= QUALVOLATILE; break; case TRESTRICT: *tq |= QUALRESTRICT; break; case T_ATOMIC: error(&tok.loc, "_Atomic type qualifier is not yet supported"); default: return 0; } next(); return 1; } /* 6.7.4 Function specifiers */ static int funcspec(enum funcspecifier *fs) { enum funcspecifier new; switch (tok.kind) { case TINLINE: new = FUNCINLINE; break; case T_NORETURN: new = FUNCNORETURN; break; default: return 0; } if (!fs) error(&tok.loc, "function specifier not allowed in this declaration"); *fs |= new; next(); return 1; } static void structdecl(struct scope *, struct type *); static struct type * tagspec(struct scope *s) { struct type *t; char *tag; enum typekind kind; struct declaration *d; uint64_t i; switch (tok.kind) { case TSTRUCT: kind = TYPESTRUCT; break; case TUNION: kind = TYPEUNION; break; case TENUM: kind = TYPEBASIC; break; default: fatal("internal error: unknown tag kind"); } next(); if (tok.kind == TIDENT) { tag = tok.lit; next(); t = scopegettag(s, tag, false); if (s->parent && !t && tok.kind != TLBRACE && (kind == TYPEBASIC || tok.kind != TSEMICOLON)) t = scopegettag(s->parent, tag, true); } else if (tok.kind != TLBRACE) { error(&tok.loc, "expected identifier or '{' after struct/union"); } else { tag = NULL; t = NULL; } if (t) { if (t->kind != kind) error(&tok.loc, "redeclaration of tag '%s' with different kind", tag); } else { t = mktype(kind, NULL); if (kind == TYPEBASIC) { *t = typeint; } else { t->repr = &i64; // XXX t->size = 0; t->align = 0; t->structunion.tag = tag; t->structunion.members = (struct array){0}; } t->incomplete = true; if (tag) scopeputtag(s, tag, t); } if (tok.kind != TLBRACE) return t; if (!t->incomplete) error(&tok.loc, "redefinition of tag '%s'", tag); next(); switch (t->kind) { case TYPESTRUCT: case TYPEUNION: while (tok.kind != TRBRACE) structdecl(s, t); next(); t->size = ALIGNUP(t->size, t->align); t->incomplete = false; break; case TYPEBASIC: /* enum */ for (i = 0; tok.kind == TIDENT; ++i) { d = mkdecl(DECLCONST, &typeint, LINKNONE); scopeputdecl(s, tok.lit, d); next(); if (consume(TASSIGN)) i = intconstexpr(s); d->value = mkintconst(t->repr, i); if (!consume(TCOMMA)) break; } expect(TRBRACE, "to close enum specifier"); t->incomplete = false; } return t; } /* 6.7 Declarations */ static struct type * declspecs(struct scope *s, enum storageclass *sc, enum funcspecifier *fs, int *align) { struct type *t; struct declaration *d; enum typespecifier ts = SPECNONE; enum typequalifier tq = QUALNONE; int ntypes = 0; uint64_t i; t = NULL; if (sc) *sc = SCNONE; if (fs) *fs = FUNCNONE; for (;;) { if (typequal(&tq) || storageclass(sc) || funcspec(fs)) continue; switch (tok.kind) { /* 6.7.2 Type specifiers */ case TVOID: t = &typevoid; ++ntypes; next(); break; case TCHAR: ts |= SPECCHAR; ++ntypes; next(); break; case TSHORT: if (ts & SPECSHORT) error(&tok.loc, "duplicate 'short'"); ts |= SPECSHORT; next(); break; case TINT: ts |= SPECINT; ++ntypes; next(); break; case TLONG: if (ts & SPECLONG2) error(&tok.loc, "too many 'long'"); if (ts & SPECLONG) ts |= SPECLONG2; ts |= SPECLONG; next(); break; case TFLOAT: ts |= SPECFLOAT; ++ntypes; next(); break; case TDOUBLE: ts |= SPECDOUBLE; ++ntypes; next(); break; case TSIGNED: if (ts & SPECSIGNED) error(&tok.loc, "duplicate 'signed'"); ts |= SPECSIGNED; next(); break; case TUNSIGNED: if (ts & SPECUNSIGNED) error(&tok.loc, "duplicate 'unsigned'"); ts |= SPECUNSIGNED; next(); break; case T_BOOL: t = &typebool; ++ntypes; next(); break; case T_COMPLEX: fatal("_Complex is not yet supported"); break; case T_ATOMIC: fatal("_Atomic is not yet supported"); break; case T__BUILTIN_VA_LIST: t = &typevalist; ++ntypes; next(); break; case TSTRUCT: case TUNION: case TENUM: t = tagspec(s); ++ntypes; break; case TIDENT: if (t || ts) goto done; d = scopegetdecl(s, tok.lit, 1); if (!d || d->kind != DECLTYPE) goto done; t = d->type; ++ntypes; next(); break; /* 6.7.5 Alignment specifier */ case T_ALIGNAS: if (!align) error(&tok.loc, "alignment specifier not allowed in this declaration"); next(); expect(TLPAREN, "after '_Alignas'"); t = typename(s); if (t) { *align = t->align; } else { i = intconstexpr(s); if (!i || i & (i - 1) || i > 16) error(&tok.loc, "invalid alignment: %d", i); *align = (int)i; } expect(TRPAREN, "to close '_Alignas' specifier"); break; default: goto done; } if (ntypes > 1 || (t && ts)) error(&tok.loc, "multiple types in declaration specifiers"); } done: switch ((int)ts) { case SPECNONE: break; case SPECCHAR: t = &typechar; break; case SPECSIGNED|SPECCHAR: t = &typeschar; break; case SPECUNSIGNED|SPECCHAR: t = &typeuchar; break; case SPECSHORT: case SPECSHORT|SPECINT: case SPECSIGNED|SPECSHORT: case SPECSIGNED|SPECSHORT|SPECINT: t = &typeshort; break; case SPECUNSIGNED|SPECSHORT: case SPECUNSIGNED|SPECSHORT|SPECINT: t = &typeushort; break; case SPECINT: case SPECSIGNED: case SPECSIGNED|SPECINT: t = &typeint; break; case SPECUNSIGNED: case SPECUNSIGNED|SPECINT: t = &typeuint; break; case SPECLONG: case SPECLONG|SPECINT: case SPECSIGNED|SPECLONG: case SPECSIGNED|SPECLONG|SPECINT: t = &typelong; break; case SPECUNSIGNED|SPECLONG: case SPECUNSIGNED|SPECLONG|SPECINT: t = &typeulong; break; case SPECLONGLONG: case SPECLONGLONG|SPECINT: case SPECSIGNED|SPECLONGLONG: case SPECSIGNED|SPECLONGLONG|SPECINT: t = &typellong; break; case SPECUNSIGNED|SPECLONGLONG: case SPECUNSIGNED|SPECLONGLONG|SPECINT: t = &typeullong; break; case SPECFLOAT: t = &typefloat; break; case SPECDOUBLE: t = &typedouble; break; case SPECLONG|SPECDOUBLE: t = &typelongdouble; break; default: error(&tok.loc, "invalid combination of type specifiers"); } if (!t && (tq || (sc && *sc) || (fs && *fs))) error(&tok.loc, "declaration has no type specifier"); return mkqualifiedtype(t, tq); } /* 6.7.6 Declarators */ static struct parameter *parameter(struct scope *); struct partialtype { struct type *outer; struct type **inner; }; static bool istypename(struct scope *s, const char *name) { struct declaration *d; d = scopegetdecl(s, tok.lit, 1); return d && d->kind == DECLTYPE; } static void declaratortypes(struct scope *s, struct partialtype *result, char **name, bool allowabstract) { struct partialtype prefix1 = {NULL, &prefix1.outer}, prefix2 = {NULL, &prefix2.outer}; struct type *t; struct parameter **p; uint64_t i; enum typequalifier tq; while (consume(TMUL)) { t = mkpointertype(result->outer); tq = QUALNONE; while (typequal(&tq)) ; if (!result->outer) result->inner = &t->base; result->outer = mkqualifiedtype(t, tq); } if (name) *name = NULL; switch (tok.kind) { case TLPAREN: next(); if (allowabstract && tok.kind != TMUL && (tok.kind != TIDENT || istypename(s, tok.lit))) goto func; declaratortypes(s, &prefix1, name, allowabstract); expect(TRPAREN, "after parenthesized declarator"); break; case TIDENT: if (!name) error(&tok.loc, "identifier not allowed in abstract declarator"); *name = tok.lit; next(); break; default: if (!allowabstract) error(&tok.loc, "expected '(' or identifier"); } for (;;) { switch (tok.kind) { case TLPAREN: /* function declarator */ next(); func: t = mktype(TYPEFUNC, NULL); t->func.isprototype = 0; t->func.isvararg = 0; t->func.isnoreturn = 0; t->func.params = NULL; p = &t->func.params; switch (tok.kind) { case TIDENT: if (!istypename(s, tok.lit)) { /* identifier-list (K&R declaration) */ do { *p = mkparam(tok.lit, NULL); p = &(*p)->next; next(); if (tok.kind != TCOMMA) break; next(); } while (tok.kind == TIDENT); break; } /* fallthrough */ default: t->func.isprototype = 1; for (;;) { *p = parameter(s); p = &(*p)->next; if (tok.kind != TCOMMA) break; next(); if (tok.kind == TELLIPSIS) { t->func.isvararg = 1; next(); break; } } if (typeunqual(t->func.params->type, NULL)->kind == TYPEVOID && !t->func.params->next) t->func.params = NULL; break; case TRPAREN: break; } expect(TRPAREN, "to close function declarator"); *prefix2.inner = t; prefix2.inner = &t->base; break; case TLBRACK: /* array declarator */ next(); tq = QUALNONE; for (;;) { if (tok.kind == TSTATIC) next(); /* ignore */ else if (!typequal(&tq)) break; } if (tok.kind == TMUL) error(&tok.loc, "VLAs are not yet supported"); if (tok.kind == TRBRACK) { i = 0; next(); } else { i = intconstexpr(s); expect(TRBRACK, "after array length"); } t = mkarraytype(NULL, i); *prefix2.inner = mkqualifiedtype(t, tq); prefix2.inner = &t->base; break; default: goto done; } } done: if (prefix2.outer) { if (result->inner == &result->outer) result->inner = prefix2.inner; *prefix2.inner = result->outer; result->outer = prefix2.outer; } if (prefix1.outer) { if (result->inner == &result->outer) result->inner = prefix1.inner; *prefix1.inner = result->outer; result->outer = prefix1.outer; } } static struct type * declarator(struct scope *s, struct type *t, char **name, bool allowabstract) { struct partialtype result = {NULL, &result.outer}; declaratortypes(s, &result, name, allowabstract); *result.inner = t; for (t = result.outer; t; t = t->base) { switch (t->kind) { case TYPEARRAY: t->align = t->base->align; t->size = t->base->size * t->array.length; // XXX: overflow? break; } } return result.outer; } static struct type * adjust(struct type *t) { enum typequalifier tq = QUALNONE; t = typeunqual(t, &tq); switch (t->kind) { case TYPEARRAY: t = mkqualifiedtype(mkpointertype(t->base), tq); break; case TYPEFUNC: t = mkpointertype(t); break; } return t; } static struct parameter * parameter(struct scope *s) { struct parameter *p; struct type *t; enum storageclass sc; t = declspecs(s, &sc, NULL, NULL); if (!t) error(&tok.loc, "no type in parameter declaration"); if (sc && sc != SCREGISTER) error(&tok.loc, "parameter declaration has invalid storage-class specifier"); p = mkparam(NULL, t); p->type = adjust(declarator(s, p->type, &p->name, true)); return p; } static bool paramdecl(struct scope *s, struct parameter *params) { struct parameter *p; struct type *t, *base; char *name; base = declspecs(s, NULL, NULL, NULL); if (!base) return false; for (;;) { t = adjust(declarator(s, base, &name, false)); for (p = params; p; p = p->next) { if (strcmp(name, p->name) == 0) { p->type = t; break; } } if (!p) error(&tok.loc, "old-style function declarator has no parameter named '%s'", name); if (tok.kind == TSEMICOLON) break; expect(TCOMMA, "or ';' after parameter declarator"); } next(); return true; } // XXX: cleanup static void structdecl(struct scope *s, struct type *t) { struct type *base; struct member *m; int basealign = 0, align; base = declspecs(s, NULL, NULL, &align); if (!base) error(&tok.loc, "no type in struct member declaration"); if (tok.kind == TSEMICOLON) { if ((base->kind != TYPESTRUCT && base->kind != TYPEUNION) || base->structunion.tag) error(&tok.loc, "struct declaration must declare at least one member"); next(); if (align < base->align) align = base->align; t->size = ALIGNUP(t->size, align); arrayforeach (&base->structunion.members, m) m->offset += t->size; arrayaddbuf(&t->structunion.members, base->structunion.members.val, base->structunion.members.len); t->size += ALIGNUP(base->size, align); if (t->align < align) t->align = align; return; } for (;;) { align = basealign; if (tok.kind != TCOLON) { m = arrayadd(&t->structunion.members, sizeof(*m)); m->type = declarator(s, base, &m->name, false); assert(m->type->align > 0); if (align < m->type->align) align = m->type->align; t->size = ALIGNUP(t->size, align); m->offset = t->size; t->size += m->type->size; if (t->align < align) t->align = align; } if (tok.kind == TCOLON) error(&tok.loc, "bit-fields are not yet supported"); if (tok.kind == TSEMICOLON) break; expect(TCOMMA, "or ';' after declarator"); } next(); } /* 6.7.7 Type names */ struct type * typename(struct scope *s) { struct type *t; t = declspecs(s, NULL, NULL, NULL); return t ? declarator(s, t, NULL, true) : NULL; } bool decl(struct scope *s, struct function *f) { struct type *t, *base; enum storageclass sc; enum funcspecifier fs; struct initializer *init; struct parameter *p; char *name; int allowfunc = !f; struct declaration *d; enum declarationkind kind; enum linkage linkage; uint64_t c; int align = 0; if (consume(T_STATIC_ASSERT)) { expect(TLPAREN, "after _Static_assert"); c = intconstexpr(s); expect(TCOMMA, "after static assertion expression"); expect(TSTRINGLIT, "after static assertion expression"); if (!c) error(&tok.loc, "static assertion failed"); // XXX: add string here expect(TRPAREN, "after static assertion message"); expect(TSEMICOLON, "after static assertion"); return true; } base = declspecs(s, &sc, &fs, &align); if (!base) return false; if (!f) { /* 6.9p2 */ if (sc & SCAUTO) error(&tok.loc, "external declaration must not contain 'auto'"); if (sc & SCREGISTER) error(&tok.loc, "external declaration must not contain 'register'"); } if (consume(TSEMICOLON)) { /* XXX 6.7p2 error unless in function parameter/struct/union, or tag/enum members are declared */ return true; } for (;;) { t = declarator(s, base, &name, false); kind = sc & SCTYPEDEF ? DECLTYPE : t->kind == TYPEFUNC ? DECLFUNC : DECLOBJECT; d = scopegetdecl(s, name, false); if (d && d->kind != kind) error(&tok.loc, "'%s' redeclared with different kind", name); switch (kind) { case DECLTYPE: if (align) error(&tok.loc, "typedef '%s' declared with alignment specifier", name); if (!d) scopeputdecl(s, name, mkdecl(DECLTYPE, t, LINKNONE)); else if (!typesame(d->type, t)) error(&tok.loc, "typedef '%s' redefined with different type", name); break; case DECLOBJECT: if (d) { if (d->linkage == LINKNONE) error(&tok.loc, "object '%s' with no linkage redeclared", name); if (!(sc & SCEXTERN)) { linkage = f ? LINKNONE : sc & SCSTATIC ? LINKINTERN : LINKEXTERN; if (d->linkage != linkage) error(&tok.loc, "object '%s' redeclared with different linkage", name); } if (!typecompatible(d->type, t)) error(&tok.loc, "object '%s' redeclared with incompatible type", name); d->type = typecomposite(t, d->type); } else { if (sc & SCEXTERN) { if (s->parent) d = scopegetdecl(s->parent, name, true); linkage = d && d->linkage != LINKNONE ? d->linkage : LINKEXTERN; d = scopegetdecl(&filescope, name, false); if (d) { if (d->linkage != linkage) error(&tok.loc, "object '%s' redeclared with different linkage", name); if (!typecompatible(d->type, t)) error(&tok.loc, "object '%s' redeclared with incompatible type", name); t = typecomposite(t, d->type); } } else { linkage = f ? LINKNONE : sc & SCSTATIC ? LINKINTERN : LINKEXTERN; } d = mkdecl(kind, t, linkage); scopeputdecl(s, name, d); if (linkage != LINKNONE || sc & SCSTATIC) d->value = mkglobal(name, linkage == LINKNONE); } if (d->align < align) d->align = align; if (consume(TASSIGN)) { if (f && d->linkage != LINKNONE) error(&tok.loc, "object '%s' with block scope and %s linkage cannot have initializer", name, d->linkage == LINKEXTERN ? "external" : "internal"); if (d->defined) error(&tok.loc, "object '%s' redefined", name); init = parseinit(s, d->type); } else { init = NULL; } if (sc & SCEXTERN) break; if (init || f) { if (d->linkage != LINKNONE || sc & SCSTATIC) emitdata(d, init); else funcinit(f, d, init); d->defined = true; if (d->tentative) { d->tentative = false; listremove(&d->link); } } else if (!d->defined && !d->tentative) { d->tentative = true; listinsert(tentativedefns.prev, &d->link); } break; case DECLFUNC: if (align) error(&tok.loc, "function '%s' declared with alignment specifier", name); t->func.isnoreturn |= fs & FUNCNORETURN; if (f && sc && sc != SCEXTERN) /* 6.7.1p7 */ error(&tok.loc, "function '%s' with block scope may only have storage class 'extern'", name); if (d) { if (!typecompatible(t, d->type)) error(&tok.loc, "function '%s' redeclared with incompatible type", name); d->type = typecomposite(t, d->type); } else { if (s->parent) d = scopegetdecl(s->parent, name, 1); if (d && d->linkage != LINKNONE) { linkage = d->linkage; if (!typecompatible(t, d->type)) error(&tok.loc, "function '%s' redeclared with incompatible type", name); t = typecomposite(t, d->type); } else { linkage = sc & SCSTATIC ? LINKINTERN : LINKEXTERN; } d = mkdecl(kind, t, linkage); d->value = mkglobal(name, false); scopeputdecl(s, name, d); } break; } switch (tok.kind) { default: if (!allowfunc || kind != DECLFUNC || t->func.isprototype) error(&tok.loc, "expected ',' or ';' after declarator"); /* K&R-style function definition */ while (paramdecl(s, t->func.params)) ; for (p = t->func.params; p; p = p->next) { if (!p->type) error(&tok.loc, "old-style function definition does not declare '%s'", p->name); } if (tok.kind != TLBRACE) error(&tok.loc, "expected compound statement after function declarator"); /* fallthrough */ case TLBRACE: if (!allowfunc) error(&tok.loc, "function declaration not allowed"); if (d->defined) error(&tok.loc, "function '%s' redefined", name); s = mkscope(&filescope); f = mkfunc(name, t, s); stmt(f, s); emitfunc(f, d->linkage == LINKEXTERN); s = delscope(s); d->defined = true; return true; case TCOMMA: next(); allowfunc = 0; break; case TSEMICOLON: next(); return true; } } } struct declaration *stringdecl(struct expression *expr) { static struct hashtable *strings; struct hashtablekey key; void **entry; struct declaration *d; if (!strings) strings = mkhtab(64); assert(expr->kind == EXPRSTRING); htabbufkey(&key, expr->string.data, expr->string.size); entry = htabput(strings, &key); d = *entry; if (!d) { d = mkdecl(DECLOBJECT, expr->type, LINKNONE); d->value = mkglobal("string", true); emitdata(d, mkinit(0, expr)); *entry = d; } return d; } void emittentativedefns(void) { struct list *l; for (l = tentativedefns.next; l != &tentativedefns; l = l->next) emitdata(listelement(l, struct declaration, link), NULL); }