aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-04 22:36:59 +0200
committerlemon <lsof@mailbox.org>2022-08-04 22:36:59 +0200
commit0fec7de747d93586eda66ce190f5f3d6715421a4 (patch)
tree2c6db93b3e6d3299d530cea20dd6468cf47b43c2
parent4b2451500b8f085321a041ebc13761a5102f0e6d (diff)
struct,unions, compound literals; mostly
-rw-r--r--bootstrap/all.h40
-rw-r--r--bootstrap/cgen.c78
-rw-r--r--bootstrap/dump.c2
-rw-r--r--bootstrap/fold.c13
-rw-r--r--bootstrap/parse.c217
-rw-r--r--bootstrap/test.cff36
-rw-r--r--bootstrap/types.c38
7 files changed, 376 insertions, 48 deletions
diff --git a/bootstrap/all.h b/bootstrap/all.h
index cb221da..bbe23b3 100644
--- a/bootstrap/all.h
+++ b/bootstrap/all.h
@@ -136,12 +136,16 @@ enum typetype {
TYslice,
TYfn,
TYenum,
+ TYstruct,
+ TYunion,
+ TYeunion,
};
struct type {
enum typetype t;
size_t size, align;
bool konst;
+ int _id;
union {
bool int_signed;
struct {
@@ -156,12 +160,22 @@ struct type {
struct {
const struct type *intty;
const char *name;
- slice_t(struct enumfield {
+ slice_t(struct enumval {
const char *name;
i64 i;
}) vals;
int id;
} enu;
+ struct {
+ const char *name;
+ slice_t(struct aggfield {
+ size_t off;
+ const char *name;
+ const struct type *ty;
+ }) flds;
+ bool fwd;
+ int id;
+ } agg;
};
// for cgen.c hack (mutated later, not hashed or involved in equality)
const char *_cname;
@@ -242,6 +256,8 @@ enum exprtype {
Eblock,
Eas,
Eenumval,
+ Ezeroini,
+ Eini,
};
struct blockstmt {
@@ -288,9 +304,20 @@ struct expr {
i64 i;
const char *vname;
} enu;
+ struct {
+ slice_t(struct iniarg) args;
+ } ini;
};
};
+struct iniarg {
+ union {
+ size_t idx;
+ const char *fld;
+ };
+ struct expr ex;
+};
+
enum stmttype {
Sblock,
Sexpr,
@@ -376,6 +403,7 @@ static const struct targ {
for (T __old = (var), *__dummy = ((var) = (new), NULL); \
!__dummy; \
__dummy++, (var) = __old)
+#define ALIGNUP(x,a) (((x) + ((a) - 1)) & -(a))
static inline u32
bswap32(u32 x) {
@@ -393,13 +421,16 @@ bswap64(u64 x) {
#define vec_slice_cpy(slice, v) \
((slice)->d = vec_compact(v), \
(slice)->n = (v)->length)
-
-#define jkhashv(h, v) jkhash(h, (void *)&(v), sizeof((v)))
-
+///
/////////////////////////////////
/** util.c **/
u32 jkhash(u32 hash, const u8 *data, size_t length);
+#define jkhashv(h, v) jkhash(h, (void *)&(v), sizeof((v)))
+static inline u32
+jkhashu32(u32 h, u32 x) {
+ return jkhashv(h, x);
+}
int addfilepath(const char *);
const char *fileid2path(int);
void *xmalloc(size_t);
@@ -426,6 +457,7 @@ extern const struct type
*ty_c_ullong;
void inittypes(void);
const struct type *interntype(struct type);
+void uninterntype(const struct type *);
bool typeeql(const struct type *lhs, const struct type *rhs);
bool completetype(const struct type *);
void putprimtypes(struct env *);
diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c
index aac0893..fe937d6 100644
--- a/bootstrap/cgen.c
+++ b/bootstrap/cgen.c
@@ -28,6 +28,9 @@ gentype(const struct type *ty) {
assert(ty->length >= 0);
case TYslice:
case TYfn:
+ case TYstruct:
+ case TYunion:
+ case TYeunion:
assert(ty->_cname);
pri("%s", ty->_cname);
return;
@@ -53,6 +56,7 @@ pristring(const char *s, u64 n) {
}
static void genexpr(struct expr *expr);
+static void geniniex(struct expr *ex);
void
pri(const char *fmt, ...) {
@@ -115,6 +119,9 @@ pri(const char *fmt, ...) {
case 'e':
genexpr(va_arg(ap, struct expr *));
break;
+ case 'n':
+ geniniex(va_arg(ap, struct expr *));
+ break;
default:
fprintf(outfp, "pri(): bad fmt '%%%c'\n", c);
abort();
@@ -127,6 +134,25 @@ pri(const char *fmt, ...) {
static void genblock(struct blockstmt block);
static void
+geniniex(struct expr *ex) {
+ if (ex->t == Ezeroini)
+ pri("{0}");
+ else {
+ assert(ex->t == Eini);
+ pri("{ ");
+ for (int i = 0; i < ex->ini.args.n; ++i) {
+ struct iniarg *arg = &ex->ini.args.d[i];
+ if (ex->ty->t == TYarr)
+ pri("[%I] = %e, ", arg->idx, arg->ex);
+ else
+ pri(".%s = %e, ", arg->fld, arg->ex);
+ }
+
+ pri("}");
+ }
+}
+
+static void
genexpr(struct expr *ex) {
const struct type *ty = unconstify(ex->ty);
switch (ex->t) {
@@ -194,6 +220,15 @@ genexpr(struct expr *ex) {
ty = ex->ty->enu.intty;
goto intlit;
break;
+ case Ezeroini:
+ if (ty->t == TYarr || ty->t == TYstruct)
+ pri("((%t){0})", ty);
+ else
+ pri("((%t)0)", ty);
+ break;
+ case Eini:
+ pri("((%t)%n)", ty, ex);
+ break;
}
}
@@ -218,8 +253,16 @@ genstmt(struct stmt *stmt) {
switch (decl.t) {
case Dlet:
pri("%t %s", decl.var.ty, decl.name);
- if (decl.var.ini)
- pri(" = %e", decl.var.ini);
+ if (decl.var.ini) {
+ pri(" = ");
+ if ((decl.var.ty->t == TYstruct || decl.var.ty->t == TYarr)
+ && (decl.var.ini->t == Ezeroini || decl.var.ini->t == Eini))
+ {
+ geniniex(decl.var.ini);
+ } else {
+ pri("%e", decl.var.ini);
+ }
+ }
pri(";\n");
break;
case Dfn:
@@ -300,6 +343,7 @@ liftnestedex(struct expr *ex) {
switch (ex->t) {
case Eintlit: case Eflolit: case Estrlit:
case Eboolit: case Enullit: case Ename:
+ case Ezeroini:
case Eenumval:
break;
case Eprefix:
@@ -332,6 +376,10 @@ liftnestedex(struct expr *ex) {
case Eas:
liftnestedex(ex->child);
break;
+ case Eini:
+ for (int i = 0; i < ex->ini.args.n; ++i)
+ liftnestedex(&ex->ini.args.d[i].ex);
+ break;
}
}
@@ -461,12 +509,13 @@ static void
defctype(const struct type *ty, void *_) {
static int id;
char **cname = (char **)&ty->_cname;
+ const char *kind;
if (!ty->_cname)
switch (ty->t) {
case TYvoid: case TYbool:
case TYint: case TYfloat:
- case TYptr:
+ case TYptr: case TYenum:
return;
case TYarr:
assert(ty->length >= 0);
@@ -495,8 +544,27 @@ defctype(const struct type *ty, void *_) {
pri("...");
pri(");\n");
break;
- case TYenum:
- *cname = xasprintf("__ty%d", id++);
+ case TYstruct:
+ kind = "struct";
+ goto agg;
+ case TYunion:
+ kind = "union";
+ agg:
+ *cname = xasprintf("__ty%s%d", ty->agg.name ? ty->agg.name : "", id++);
+ pri("typedef %s %s %s;\n", kind, *cname, *cname);
+ if (!ty->agg.fwd) {
+ pri("%s %s {\n", kind, *cname);
+ for (int i = 0; i < ty->agg.flds.n; ++i) {
+ struct aggfield fld = ty->agg.flds.d[i];
+ pri("%t %s;\n", fld.ty, fld.name);
+ }
+ pri("};\n");
+ pri("_Static_assert(sizeof(%s) == %U);\n", *cname, (long long)ty->size);
+ pri("_Static_assert(__alignof__(%s) == %U);\n", *cname, (long long)ty->align);
+ }
+ break;
+ case TYeunion:
+ break;
}
}
diff --git a/bootstrap/dump.c b/bootstrap/dump.c
index d01f4a7..0ea5c07 100644
--- a/bootstrap/dump.c
+++ b/bootstrap/dump.c
@@ -41,7 +41,7 @@ pritype(const struct type *ty) {
if (ty->fn.variadic)
pri("...");
pri(") %t", ty->fn.retty);
- case TYenum:
+ default:assert(0);
}
}
diff --git a/bootstrap/fold.c b/bootstrap/fold.c
index f7962d4..efe1f83 100644
--- a/bootstrap/fold.c
+++ b/bootstrap/fold.c
@@ -238,6 +238,17 @@ fas(struct expr *ex) {
free(child);
}
+static void
+fzeroini(struct expr *ex) {
+ const struct type *ty = ex->ty;
+ if (ty->t == TYint || ty->t == TYfloat || ty->t == TYbool || ty->t == TYenum) {
+ ex->i = 0;
+ numcast(ex, ty);
+ if (ty->t == TYenum)
+ trysetenumvname(ex);
+ }
+}
+
int
fold(struct expr *ex) {
switch (ex->t) {
@@ -261,6 +272,8 @@ fold(struct expr *ex) {
case Eas:
fas(ex);
break;
+ case Ezeroini:
+ fzeroini(ex);
default:
break;
}
diff --git a/bootstrap/parse.c b/bootstrap/parse.c
index 71e2a74..659f756 100644
--- a/bootstrap/parse.c
+++ b/bootstrap/parse.c
@@ -531,12 +531,12 @@ parsetype(struct parser *P) {
struct tok tok;
if (lexmatch(P, &tok, '*')) {
return interntype((struct type) {
- TYptr,
- g_targ.ptrsize,
+ TYptr, g_targ.ptrsize,
.child = parsetype(P)
});
} else if (lexmatch(P, &tok, '[')) {
i64 length = -1;
+ const struct type *child;
if (!lexmatch(P, &tok, ']')) {
struct expr ex = parseexpr(P);
if (!fold(&ex) || ex.t != Eintlit)
@@ -546,11 +546,12 @@ parsetype(struct parser *P) {
fatal(P, ex.span, "negative array length");
lexexpect(P, ']');
}
+ child = parsetype(P);
+ if (!completetype(child))
+ fatal(P, tok.span, "array of incomplete type");
return interntype((struct type) {
- TYarr,
- g_targ.ptrsize,
- .child = parsetype(P),
- .length = length
+ TYarr, length * child->size, child->align,
+ .child = child, .length = length
});
} else if (lexmatch(P, &tok, TKkw_const)) {
return constify(parsetype(P));
@@ -626,6 +627,111 @@ mkarraytype(const struct type *child, size_t n) {
});
}
+static struct aggfield *
+structidx2fld(const struct type *ty, int idx) {
+ if (idx < 0 || idx >= ty->agg.flds.n)
+ return NULL;
+ return &ty->agg.flds.d[idx];
+}
+
+static int
+structfldnam2idx(const struct type *ty, const char *name) {
+ int i;
+ assert(name);
+ for (i = 0; i < ty->agg.flds.n; ++i)
+ if (!strcmp(name, ty->agg.flds.d[i].name))
+ return i;
+ return -1;
+}
+
+static struct expr
+parsestructini(struct parser *P, const struct type *ty) {
+ struct expr ex = {Eini};
+ struct tok tok;
+ vec_t(struct iniarg) args = {0};
+ int idx = 0;
+ struct aggfield *fld;
+
+ ex.span = lexpeek(P).span;
+
+ while (!lexmatch(P, &tok, '}')) {
+ struct expr e;
+
+ if (lexmatch(P, &tok, '.')) {
+ const char *fnam = (tok = lexexpect(P, TKident)).str;
+ lexexpect(P, ':');
+ idx = structfldnam2idx(ty, fnam);
+ if (idx < 0)
+ fatal(P, tok.span, "struct has no field `%s'", fnam);
+ }
+
+ fld = structidx2fld(ty, idx++);
+ e = parseexpr(P);
+ if (!fld)
+ fatal(P, e.span,
+ "excess elements in struct initializer");
+ if (!typeof2(e.ty, fld->ty))
+ fatal(P, e.span, "incompatible element type in struct initializer");
+
+ vec_push(&args, ((struct iniarg) {
+ .fld = fld->name,
+ e
+ }));
+
+ if (!lexmatch(P, &tok, ',')) {
+ lexexpect(P, '}');
+ break;
+ }
+ }
+
+ ex.ty = ty;
+ vec_slice_cpy(&ex.ini.args, &args);
+ return ex;
+}
+
+static struct expr
+parsearrini(struct parser *P, const struct type *ty) {
+ struct expr ex = {Eini};
+ struct tok tok;
+ vec_t(struct iniarg) args = {0};
+ i64 iota = 0;
+
+ ex.span = lexpeek(P).span;
+
+ while (!lexmatch(P, &tok, '}')) {
+ struct expr e;
+
+ if (lexmatch(P, &tok, '[')) {
+ struct expr i = parseexpr(P);
+ if (!fold(&i) || i.t != Eintlit)
+ fatal(P, i.span,
+ "array initializer element index not a compile-time integer");
+ iota = i.i;
+ if (iota < 0)
+ fatal(P, i.span, "array initializer element index is negative");
+ lexexpect(P, ']');
+ lexexpect(P, '=');
+ }
+
+ e = parseexpr(P);
+ if (!typeof2(ty->child, e.ty))
+ fatal(P, e.span, "incompatible element type in array initializer");
+ if (ty->length >= 0 && iota >= ty->length)
+ fatal(P, e.span,
+ "excess elements in array initializer");
+ vec_push(&args, ((struct iniarg) { .idx = iota++, e }));
+
+ if (!lexmatch(P, &tok, ',')) {
+ lexexpect(P, '}');
+ break;
+ }
+ }
+
+ ex.ty = ty;
+ vec_slice_cpy(&ex.ini.args, &args);
+ return ex;
+}
+
static struct expr
pexprimary(struct parser *P) {
struct expr ex = {0};
@@ -736,6 +842,16 @@ pexprimary(struct parser *P) {
ex.t = Eenumval;
ex.enu.vname = vname;
ex.enu.i = ex.ty->enu.vals.d[i].i;
+ } else if (lexmatch(P, &tok, '{')) {
+ P->used_targetty = 1;
+ if (!P->targty)
+ fatal(P, tok.span, "cannot infer type for compound initializer");
+ if (lexmatch(P, &tok, '}'))
+ ex.t = Ezeroini, ex.ty = P->targty;
+ else if (P->targty->t == TYstruct)
+ ex = parsestructini(P, P->targty);
+ else if (P->targty->t == TYarr)
+ ex = parsearrini(P, P->targty);
} else {
experr:
fatal(P, tok.span, "expected expression (near %s)", tok2str(tok));
@@ -1176,8 +1292,9 @@ parsevardecl(struct parser *P, struct decl *decl) {
}
}
- if (ini && decl->t == Dstatic && !fold(ini))
- fatal(P, ini->span, "static initializer isn't constant");
+ // TODO static initialzier constants are a superset of folded constants
+ // if (ini && decl->t == Dstatic && !fold(ini))
+ // fatal(P, ini->span, "static initializer isn't constant");
if (ini && !typeof2(ty, ini->ty))
fatal(P, tok.span, "incompatible initializer type");
@@ -1585,6 +1702,9 @@ parsefn(struct decl *decl, struct parser *P) {
}
vec_slice_cpy(&fn->params, &params);
fn->retty = unconstify(parsetype(P));
+ if (fn->retty != ty_void && !completetype(fn->retty)) {
+ fatal(P, tok.span, "return type is incomplette");
+ }
fn->selfty = fntype(fn);
if (!lexmatch(P, &tok, ';')) {
struct env *env = xcalloc(1, sizeof *env);
@@ -1732,7 +1852,7 @@ parseenum(struct parser *P, const char *name) {
struct type ty = {TYenum};
static int id = 0;
i64 iota = 0, max = 0, min = 0;
- vec_t(struct enumfield) vals = {0};
+ vec_t(struct enumval) vals = {0};
if (lexmatch(P, &tok, ':')) {
ty.enu.intty = unconstify(parsetype(P));
@@ -1760,7 +1880,7 @@ parseenum(struct parser *P, const char *name) {
if (val > max)
max = val;
iota = val + 1;
- vec_push(&vals, ((struct enumfield) { fnam, val }));
+ vec_push(&vals, ((struct enumval) { fnam, val }));
if (!lexmatch(P, &tok, ',')) {
lexexpect(P, '}');
@@ -1779,10 +1899,56 @@ parseenum(struct parser *P, const char *name) {
assert(ty.enu.intty);
}
+ ty.size = ty.enu.intty->size;
+ ty.align = ty.enu.intty->align;
ty.enu.name = name;
ty.enu.id = id++;
vec_slice_cpy(&ty.enu.vals, &vals);
+ return interntype(ty);
+}
+
+static const struct type *
+parseagg(struct parser *P, const char *name, int kind) {
+ struct tok tok;
+ struct type ty = {kind};
+ static int id = 0;
+ size_t size = 0, align = 1;
+ vec_t(struct aggfield) flds = {0};
+
+ if (lexmatch(P, &tok, ';')) {
+ ty.agg.fwd = 1;
+ } else {
+ lexexpect(P, '{');
+ while (!lexmatch(P, &tok, '}')) {
+ const char *fnam = (tok = lexexpect(P, TKident)).str;
+ const struct type *ty = parsetype(P);
+ size_t off = kind == TYunion ? 0 : ALIGNUP(size, ty->align);
+
+ if (!completetype(ty))
+ fatal(P, tok.span, "aggregate field `%s' is of incomplete type", fnam);
+
+ align = MAX(align, ty->align);
+ if (kind == TYstruct)
+ size = off + ty->size;
+ else
+ size = MAX(size, ty->size);
+
+ vec_push(&flds, ((struct aggfield) {
+ off, fnam, ty
+ }));
+
+ if (!lexmatch(P, &tok, ',')) {
+ lexexpect(P, '}');
+ break;
+ }
+ }
+ }
+ ty.size = ALIGNUP(size, align);
+ ty.align = align;
+ ty.agg.name = name;
+ vec_slice_cpy(&ty.agg.flds, &flds);
+ ty.agg.id = id++;
return interntype(ty);
}
@@ -1790,7 +1956,8 @@ static void
parsedecl(struct decl *decl, struct parser *P, bool toplevel) {
struct tok tok = { .span = P->tokspan };
bool externp = 0;
- const char *name ;
+ const char *name;
+ int kind;
memset(decl, 0, sizeof *decl);
if (lexmatch(P, &tok, TKkw_extern))
@@ -1834,14 +2001,38 @@ parsedecl(struct decl *decl, struct parser *P, bool toplevel) {
if (externp) fatal(P, tok.span, "enum cannot be `extern'");
decl->name = lexexpects(P, TKident, "enum name").str;
decl->ty = parseenum(P, decl->name);
-
+ } else if (lexmatch(P, &tok, TKkw_struct)) {
+ struct decl *d2;
+ kind = TYstruct;
+ if (externp) fatal(P, tok.span, "struct cannot be `extern'");
+ agg:
+ decl->name = lexexpects(P, TKident, "struct name").str;
+ decl->ty = parseagg(P, decl->name, kind);
+ d2 = (struct decl *)finddecl(P, decl->name);
+ if (d2 && d2->t == Dtype && d2->ty->t == kind && d2->ty->agg.fwd) {
+ // modify existing forward declaration
+ *(size_t *)&d2->ty->size = decl->ty->size;
+ *(size_t *)&d2->ty->align = decl->ty->align;
+ *(bool *)&d2->ty->agg.fwd = 0;
+ memcpy((void *)&d2->ty->agg.flds, &decl->ty->agg.flds,
+ sizeof d2->ty->agg.flds);
+ uninterntype(decl->ty);
+ decl = d2;
+ envput(P->curenv, d2);
+ goto noputdecl;
+ }
+ } else if (lexmatch(P, &tok, TKkw_union)) {
+ if (externp) fatal(P, tok.span, "union cannot be `extern'");
+ kind = TYunion;
+ goto agg;
} else {
fatal(P, tok.span, "expected declaration (near %s)",
tok2str(tok));
}
- decl->span = tok.span;
putdecl(P, tok.span, decl);
+noputdecl:
+ decl->span = tok.span;
}
void
diff --git a/bootstrap/test.cff b/bootstrap/test.cff
index 89a21eb..651c2ce 100644
--- a/bootstrap/test.cff
+++ b/bootstrap/test.cff
@@ -1,30 +1,28 @@
-typedef v3f ["!"[~-1] - as(i16)30.4f64]f32;
-enum Color {
- Red = 7,
- Green = -10,
- Blue,
+struct Vec2f;
+struct Vec2f {
+ x f32,
+ y f32,
}
-fn best() Color {
- return :Green;
+union Val {
+ x i64,
+ lo i32,
+ y f64,
}
-fn hex(c Color) u32 {
- switch (c) {
- case :Red; return 0xFF0000;
- case :Green; return 0x00FF00;
- case :Blue; return 0x0000FF;
- }
-}
+static xs *void = {};
extern fn main (argc int, argv **u8) void {
extern fn printf(fmt *const u8, ...) int;
- printf("red %d\n", Color:Red);
- printf("green %d\n", Color:Green);
- printf("blue %d\n", Color:Blue);
- printf("red ! %.8X\n", hex(:Red));
-
+ let x Vec2f = { .y: 1, .x: 2.4 };
+ printf("v = { %g, %g }\n", x.x, x.y);
+
+ let is [10]int = { [4] = 1, 2, [1 - 1] = 3 };
+ for let i = 0; i < 10; ++i {
+ printf("%d\n", is[i]);
+ }
+
return;
}
diff --git a/bootstrap/types.c b/bootstrap/types.c
index 0353041..7491b4b 100644
--- a/bootstrap/types.c
+++ b/bootstrap/types.c
@@ -23,7 +23,7 @@ inittypes() {
free(types.buckets);
types.size = 0;
- types.buckets = calloc(types.nbuckets = 16, sizeof(struct typesnode *));
+ types.buckets = calloc(types.nbuckets = 64, sizeof(struct typesnode *));
}
static u32
@@ -41,20 +41,23 @@ hashtype(const struct type *ty) {
break;
case TYptr:
case TYslice:
- h = jkhashv(h, ty->child);
+ h = jkhashv(h, ty->child->_id);
break;
case TYarr:
- h = jkhashv(h, ty->child);
+ h = jkhashv(h, ty->child->_id);
h = jkhashv(h, ty->length);
break;
case TYfn:
- h = jkhashv(h, ty->fn.retty);
+ h = jkhashv(h, ty->fn.retty->_id);
h = jkhashv(h, ty->fn.params.n);
for (int i = 0; i < ty->fn.params.n; ++i)
- h = jkhashv(h, ty->fn.params.d[i]);
+ h = jkhashv(h, ty->fn.params.d[i]->_id);
case TYenum:
h = jkhashv(h, ty->enu.id);
break;
+ case TYstruct: case TYunion: case TYeunion:
+ h = jkhashv(h, ty->agg.id);
+ break;
}
return h;
}
@@ -85,7 +88,9 @@ typeeql(const struct type *lhs, const struct type *rhs) {
return 0;
return 1;
case TYenum:
- return 0;
+ return lhs->enu.id == rhs->enu.id;
+ case TYstruct: case TYunion: case TYeunion:
+ return lhs->agg.id == rhs->agg.id;
}
assert(0 && "unreachable");
}
@@ -117,16 +122,35 @@ typesput(const struct type *key) {
const struct type *
interntype(struct type ty) {
+ static int id;
if (!ty.align)
ty.align = ty.size;
const struct type *ty2 = typesfind(&ty);
if (ty2)
return ty2;
+ ty._id = id++;
ty2 = typesput(&ty);
assert(ty2);
return ty2;
}
+void
+uninterntype(const struct type *key) {
+ u32 hash = hashtype(key);
+ size_t idx = hash & (types.nbuckets - 1);
+ for (struct typesnode *n = types.buckets[idx], *prev = NULL; n; n = n->next) {
+ if (key == &n->ty) {
+ if (prev) {
+ prev->next = n->next;
+ } else {
+ types.buckets[idx] = n->next;
+ }
+ break;
+ }
+ prev = n;
+ }
+}
+
bool
completetype(const struct type *ty) {
if (ty->t == TYvoid)
@@ -135,6 +159,8 @@ completetype(const struct type *ty) {
return 0;
if (ty->t == TYarr && ty->length < 0)
return 0;
+ if (ty->t == TYstruct || ty->t == TYunion || ty->t == TYeunion)
+ return !ty->agg.fwd;
return 1;
}