diff options
author | Michael Forney <mforney@mforney.org> | 2019-02-11 18:43:18 -0800 |
---|---|---|
committer | Michael Forney <mforney@mforney.org> | 2019-02-12 01:55:14 -0800 |
commit | eddc4693e49f70cd214b7645cb9fce54a89fbb6c (patch) | |
tree | fa1b640f49cde25e323aa0629aed64c064da930e /decl.c |
Initial import
Diffstat (limited to 'decl.c')
-rw-r--r-- | decl.c | 897 |
1 files changed, 897 insertions, 0 deletions
@@ -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); +} |