#include #include #include #include #include #include #include #include "util.h" #include "decl.h" #include "expr.h" #include "init.h" #include "pp.h" #include "token.h" #include "type.h" struct object { uint64_t offset; struct type *type; union { struct member *mem; size_t idx; }; bool iscur; }; struct initparser { /* TODO: keep track of type depth, and allocate maximum possible number of nested objects in initializer */ struct object obj[32], *cur, *sub; }; struct initializer * mkinit(uint64_t start, uint64_t end, struct expression *expr) { struct initializer *init; init = xmalloc(sizeof(*init)); init->start = start; init->end = end; init->expr = expr; init->next = NULL; return init; } static void initadd(struct initializer **init, struct initializer *new) { struct initializer *next, *sub, *last; uint64_t offset; while (*init && new->start >= (*init)->end) init = &(*init)->next; next = *init; if (next && next->start <= new->start && new->end < next->end) { initadd(&next->subinit, new); last = NULL; /* silence gcc, we know that next->subinit has at least one member */ for (offset = next->start, sub = next->subinit; sub; offset = sub->end, last = sub, sub = sub->next) { if (sub->start != offset) return; } if (offset == next->end) { *init = next->subinit; last->next = next->next; } } else { *init = new; while (next && next->start < (*init)->end) next = next->next; (*init)->next = next; } } static void updatearray(struct type *t, uint64_t i) { if (!t->incomplete) return; if (++i > t->array.length) { t->array.length = i; t->size = i * t->base->size; } } static void subobj(struct initparser *p, struct type *t, uint64_t off) { off += p->sub->offset; if (++p->sub == p->obj + LEN(p->obj)) fatal("internal error: too many designators"); p->sub->type = typeunqual(t, NULL); p->sub->offset = off; p->sub->iscur = false; } static bool findmember(struct initparser *p, char *name) { struct member *m; for (m = p->sub->type->structunion.members; m; m = m->next) { if (m->name) { if (strcmp(m->name, name) == 0) { p->sub->mem = m; subobj(p, m->type, m->offset); return true; } } else { subobj(p, m->type, m->offset); if (findmember(p, name)) return true; --p->sub; } } return false; } static void designator(struct scope *s, struct initparser *p) { struct type *t; char *name; p->sub = p->cur; for (;;) { t = p->sub->type; switch (tok.kind) { case TLBRACK: if (t->kind != TYPEARRAY) error(&tok.loc, "index designator is only valid for array types"); next(); p->sub->idx = intconstexpr(s); if (t->incomplete) updatearray(t, p->sub->idx); else if (p->sub->idx >= t->array.length) error(&tok.loc, "index designator is larger than array length"); expect(TRBRACK, "for index designator"); subobj(p, t->base, p->sub->idx * t->base->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"); if (!findmember(p, name)) error(&tok.loc, "%s has no member named '%s'", t->kind == TYPEUNION ? "union" : "struct", name); free(name); break; default: expect(TASSIGN, "after designator"); return; } } } static void focus(struct initparser *p) { struct type *t; switch (p->sub->type->kind) { case TYPEARRAY: p->sub->idx = 0; if (p->sub->type->incomplete) updatearray(p->sub->type, p->sub->idx); t = p->sub->type->base; break; case TYPESTRUCT: case TYPEUNION: p->sub->mem = p->sub->type->structunion.members; t = p->sub->mem->type; break; default: t = p->sub->type; } subobj(p, t, 0); } static void advance(struct initparser *p) { struct type *t; for (;;) { --p->sub; t = p->sub->type; switch (t->kind) { case TYPEARRAY: ++p->sub->idx; if (t->incomplete) updatearray(t, p->sub->idx); if (p->sub->idx < t->array.length) { subobj(p, t->base, t->base->size * p->sub->idx); return; } break; case TYPESTRUCT: p->sub->mem = p->sub->mem->next; if (p->sub->mem) { subobj(p, p->sub->mem->type, p->sub->mem->offset); return; } break; } if (p->sub == p->cur) error(&tok.loc, "too many initializers for type"); } } /* 6.7.9 Initialization */ struct initializer * parseinit(struct scope *s, struct type *t) { struct initparser p; struct initializer *init = NULL; struct expression *expr; struct type *base; p.cur = NULL; p.sub = p.obj; p.sub->offset = 0; p.sub->type = typeunqual(t, NULL); p.sub->iscur = false; for (;;) { if (p.cur) { if (tok.kind == TLBRACK || tok.kind == TPERIOD) designator(s, &p); else if (p.sub != p.cur) advance(&p); else focus(&p); } if (tok.kind == TLBRACE) { next(); if (p.cur && p.cur->type == p.sub->type) error(&tok.loc, "nested braces around scalar initializer"); p.cur = p.sub; p.cur->iscur = true; continue; } expr = assignexpr(s); for (;;) { t = p.sub->type; switch (t->kind) { case TYPEARRAY: if (expr->flags & EXPRFLAG_DECAYED && expr->unary.base->kind == EXPRSTRING) { expr = expr->unary.base; base = typeunqual(t->base, NULL); if (!typecompatible(expr->type->base, base)) error(&tok.loc, "array initializer is string literal with incompatible type"); if (t->incomplete) updatearray(t, expr->string.size + 1); goto add; } break; case TYPESTRUCT: case TYPEUNION: if (typecompatible(expr->type, t)) goto add; break; default: /* scalar type */ assert(typeprop(t) & PROPSCALAR); expr = exprconvert(expr, t); goto add; } focus(&p); } add: initadd(&init, mkinit(p.sub->offset, p.sub->offset + p.sub->type->size, expr)); for (;;) { if (p.sub->type->kind == TYPEARRAY && p.sub->type->incomplete) p.sub->type->incomplete = false; if (!p.cur) return init; if (tok.kind == TCOMMA) { next(); if (tok.kind != TRBRACE) break; } else if (tok.kind != TRBRACE) { error(&tok.loc, "expected ',' or '}' after initializer"); } next(); p.sub = p.cur; do p.cur = p.cur == p.obj ? NULL : p.cur - 1; while (p.cur && !p.cur->iscur); } } }