aboutsummaryrefslogtreecommitdiff
path: root/decl.c
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2019-02-11 18:43:18 -0800
committerMichael Forney <mforney@mforney.org>2019-02-12 01:55:14 -0800
commiteddc4693e49f70cd214b7645cb9fce54a89fbb6c (patch)
treefa1b640f49cde25e323aa0629aed64c064da930e /decl.c
Initial import
Diffstat (limited to 'decl.c')
-rw-r--r--decl.c897
1 files changed, 897 insertions, 0 deletions
diff --git a/decl.c b/decl.c
new file mode 100644
index 0000000..bd9090b
--- /dev/null
+++ b/decl.c
@@ -0,0 +1,897 @@
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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);
+}