diff options
| author | 2022-08-05 05:35:57 +0200 | |
|---|---|---|
| committer | 2022-08-05 05:35:57 +0200 | |
| commit | fc29f3f2b0c3a7c5ef1c75d910cf0815d2edbba2 (patch) | |
| tree | eef251348f3e1b3a30a3a6296fa05e36971f3140 | |
| parent | b0d95956fcade40a2d608ccea79e2e989f97b72f (diff) | |
better diagnostics
| -rw-r--r-- | bootstrap/all.h | 2 | ||||
| -rw-r--r-- | bootstrap/dump.c | 177 | ||||
| -rw-r--r-- | bootstrap/parse.c | 190 | ||||
| -rw-r--r-- | bootstrap/test.cff | 4 | ||||
| -rw-r--r-- | bootstrap/util.c | 10 |
5 files changed, 224 insertions, 159 deletions
diff --git a/bootstrap/all.h b/bootstrap/all.h index c2a8843..b01bce7 100644 --- a/bootstrap/all.h +++ b/bootstrap/all.h @@ -485,6 +485,8 @@ void dumptransunit(const struct transunit *); const char *tokt2str(int); const char *tok2str(struct tok); void pritoktree(struct toktree); +void epri(const char *fmt, ...); +void vepri(const char *fmt, va_list); /** cgen.c **/ void cgen(FILE *, const struct transunit *); diff --git a/bootstrap/dump.c b/bootstrap/dump.c index 0ea5c07..ced692d 100644 --- a/bootstrap/dump.c +++ b/bootstrap/dump.c @@ -1,47 +1,57 @@ #include "all.h" #include <stdarg.h> -static void pri(const char *fmt, ...); - static void pritype(const struct type *ty) { assert(ty); if (ty->konst) - pri("const "); + epri("const "); switch (ty->t) { case TYvoid: - pri("void"); + epri("void"); break; case TYbool: - pri("bool"); + epri("bool"); break; case TYint: - pri("%c%z", ty->int_signed ? 'i' : 'u', + epri("%c%z", ty->int_signed ? 'i' : 'u', 8 * ty->size); break; case TYfloat: - pri("f%z", 8 * ty->size); + epri("f%z", 8 * ty->size); break; case TYptr: - pri("*%t", ty->child); + epri("*%t", ty->child); break; case TYarr: assert(ty->length >= 0); - pri("[%U]%t", ty->length, ty->child); + epri("[%U]%t", ty->length, ty->child); break; case TYslice: - pri("[#]%t", ty->child); + epri("[#]%t", ty->child); break; case TYfn: - pri("fn("); + epri("fn("); for (int i = 0; i < ty->fn.params.n; ++i) { - pri("_ %t", ty->fn.params.d[i]); - pri(", "); + epri("_ %t", ty->fn.params.d[i]); + epri(", "); } if (ty->fn.variadic) - pri("..."); - pri(") %t", ty->fn.retty); - default:assert(0); + epri("..."); + epri(") %t", ty->fn.retty); + break; + case TYenum: + epri("%s", ty->enu.name ? ty->enu.name : "(anonymous enum)"); + break; + case TYstruct: + epri("%s", ty->agg.name ? ty->agg.name : "(anonymous struct)"); + break; + case TYunion: + epri("%s", ty->agg.name ? ty->agg.name : "(anonymous union)"); + break; + case TYeunion: + epri("%s", ty->agg.name ? ty->agg.name : "(anonymous tagged union)"); + break; } } @@ -50,14 +60,14 @@ static void dumpexpr(const struct expr *expr); static void pristring(const char *s, u64 n) { extern int isprint(int); - pri("\""); + epri("\""); for (int i = 0; i < n; ++i) { if (isprint(s[i])) - pri("%c", s[i]); + epri("%c", s[i]); else fprintf(stderr, "\\%.3o", s[i]); } - pri("\""); + epri("\""); } @@ -148,13 +158,11 @@ pritoktree(struct toktree toks) { void -pri(const char *fmt, ...) { - va_list ap; +vepri(const char *fmt, va_list ap) { unsigned ch; const char *S; int ws; - va_start(ap, fmt); for (char c; (c = *fmt++);) { if (c != '%') { fputc(c, stderr); @@ -186,6 +194,10 @@ pri(const char *fmt, ...) { case 's': fprintf(stderr, "%s", va_arg(ap, const char *)); break; + case 'q': + S = va_arg(ap, const char *); + pristring(S, strlen(S)); + break; case 'w': ws = va_arg(ap, int); for (int i = 0; i < ws; ++i) @@ -196,7 +208,15 @@ pri(const char *fmt, ...) { pristring(S, va_arg(ap, u64)); break; case 't': + epri("\x1b[1m"); pritype(va_arg(ap, const struct type *)); + epri("\x1b[0m"); + break; + case 'T': + fprintf(stderr, "%s", tok2str(va_arg(ap, struct tok))); + break; + case 'k': + fprintf(stderr, "%s", tokt2str(va_arg(ap, int))); break; case 'e': dumpexpr(va_arg(ap, const struct expr *)); @@ -207,52 +227,59 @@ pri(const char *fmt, ...) { break; } } +} + +void +epri(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vepri(fmt, ap); va_end(ap); } static void dumpexpr(const struct expr *expr) { switch (expr->t) { case Eintlit: - pri("%U_%t", expr->i, expr->ty); + epri("%U_%t", expr->i, expr->ty); break; case Eflolit: - pri("%f%s", expr->f, expr->ty->size == 4 ? "f" : ""); + epri("%f%s", expr->f, expr->ty->size == 4 ? "f" : ""); break; case Estrlit: - pri("%S", expr->strlit.d, expr->strlit.n); + epri("%S", expr->strlit.d, expr->strlit.n); break; case Eboolit: - pri("#%c", expr->i ? 't' : 'f'); + epri("#%c", expr->i ? 't' : 'f'); break; case Enullit: - pri("#null"); + epri("#null"); break; case Ename: - pri("%s", expr->ref->name); + epri("%s", expr->ref->name); break; case Ebinop: - pri("(%e %c %e)", expr->binop.lhs, expr->binop.op, expr->binop.rhs); + epri("(%e %c %e)", expr->binop.lhs, expr->binop.op, expr->binop.rhs); break; case Eprefix: - pri("%c(%e)", expr->unop.op, expr->unop.child); + epri("%c(%e)", expr->unop.op, expr->unop.child); break; case Epostfix: - pri("(%e)%c", expr->unop.child, expr->unop.op); + epri("(%e)%c", expr->unop.child, expr->unop.op); break; case Econd: - pri("(%e) ? (%e) : (%e)", expr->cond.test, expr->cond.t, expr->cond.f); + epri("(%e) ? (%e) : (%e)", expr->cond.test, expr->cond.t, expr->cond.f); break; case Ecall: - pri("%e(", expr->call.callee); + epri("%e(", expr->call.callee); for (int i = 0; i < expr->call.args.n; ++i) { - pri("%e", &expr->call.args.d[i]); + epri("%e", &expr->call.args.d[i]); if (i < expr->call.args.n - 1) - pri(", "); + epri(", "); } - pri(")"); + epri(")"); break; case Eindex: - pri("%e[%e]", expr->index.lhs, expr->index.rhs); + epri("%e[%e]", expr->index.lhs, expr->index.rhs); break; default: assert(0 && "exprbad"); @@ -264,10 +291,10 @@ static void dumpstmt(const struct stmt *stmt, int ws); static void dumpblock(const struct blockstmt *block, int ws) { - pri("%w{\n", ws -1); + epri("%w{\n", ws -1); for (int i = 0; i < block->stmts.n; ++i) dumpstmt(&block->stmts.d[i], ws + 1); - pri("%w}\n", ws - 1); + epri("%w}\n", ws - 1); } static void @@ -280,101 +307,101 @@ dumpstmt(const struct stmt *stmt, int ws) { dumpdecl(&stmt->decl, ws); break; case Sexpr: - pri("%w%e\n", ws, &stmt->expr); + epri("%w%e\n", ws, &stmt->expr); break; case Sifelse: - pri("%wif (%e)\n", ws, &stmt->ifelse.test); + epri("%wif (%e)\n", ws, &stmt->ifelse.test); dumpblock(&stmt->ifelse.t, ws + 1); if (stmt->ifelse.f) { - pri("%welse\n", ws); + epri("%welse\n", ws); dumpstmt(stmt->ifelse.f, ws + 1); } break; case Swhile: - pri("%wwhile %e\n", ws, &stmt->loop.test); + epri("%wwhile %e\n", ws, &stmt->loop.test); dumpblock(&stmt->loop.body, ws + 1); break; case Sfor: - pri("%wfor \n", ws); + epri("%wfor \n", ws); if (stmt->loop.ini) dumpstmt(stmt->loop.ini, ws + 1); - pri("%w; %e; ", ws + 1, &stmt->loop.test); + epri("%w; %e; ", ws + 1, &stmt->loop.test); if (stmt->loop.next) - pri("%e", stmt->loop.next); - pri("\n"); + epri("%e", stmt->loop.next); + epri("\n"); dumpblock(&stmt->loop.body, ws + 1); break; case Siswitch: - pri("%wswitch %e {\n", ws, &stmt->iswitch.test); + epri("%wswitch %e {\n", ws, &stmt->iswitch.test); for (int i = 0; i < stmt->iswitch.cs.n; ++i) { struct iswitchcase *c = &stmt->iswitch.cs.d[i]; - pri("%wcase ", ws); + epri("%wcase ", ws); for (int j = 0; j < c->es.n; ++j) - pri("%e, ", &c->es.d[j]); - pri("do\n"); + epri("%e, ", &c->es.d[j]); + epri("do\n"); dumpblock(&c->t, ws + 1); } if (stmt->iswitch.f) { - pri("%wcase else\n", ws); + epri("%wcase else\n", ws); dumpblock(stmt->iswitch.f, ws + 1); } - pri("%w}\n", ws); + epri("%w}\n", ws); break; case Sreturn: if (stmt->retex) - pri("%wreturn %e;\n", ws, stmt->retex); + epri("%wreturn %e;\n", ws, stmt->retex); else - pri("%wreturn;\n", ws); + epri("%wreturn;\n", ws); break; } } static void dumpdecl(const struct decl *decl, int ws) { - pri("%w", ws); - pri("decl %s ", decl->name); + epri("%w", ws); + epri("decl %s ", decl->name); switch (decl->t) { case Dtype: - pri("<type> %t\n", decl->ty); + epri("<type> %t\n", decl->ty); break; case Dlet: case Dstatic: - pri("<var> %t", decl->var.ty); + epri("<var> %t", decl->var.ty); if (decl->var.ini) - pri(" = %e", decl->var.ini); - pri("\n"); + epri(" = %e", decl->var.ini); + epri("\n"); break; case Dfn: - pri("<%sfn> (", decl->externp ? "extern " : ""); + epri("<%sfn> (", decl->externp ? "extern " : ""); for (int i = 0; i < decl->fn.params.n; ++i) - pri("%s %t, ", decl->fn.params.d[i].name, decl->fn.params.d[i].ty); + epri("%s %t, ", decl->fn.params.d[i].name, decl->fn.params.d[i].ty); if (decl->fn.variadic) - pri("..."); - pri(") %t", decl->fn.retty); + epri("..."); + epri(") %t", decl->fn.retty); if (decl->fn.body) { - pri("\n"); + epri("\n"); dumpstmt(decl->fn.body, ws + 1); } else { - pri(" <...>\n"); + epri(" <...>\n"); } break; case Dmacro: - pri("<macro> {\n"); + epri("<macro> {\n"); for (int i = 0; i < decl->macro.cs.n; ++i) { struct macrocase c = decl->macro.cs.d[i]; - pri("("); + epri("("); for (int j = 0; j < c.params.n; ++j) { if (j == c.params.n - 1 && c.variadic) - pri("..."); - pri("%s", c.params.d[j]); + epri("..."); + epri("%s", c.params.d[j]); if (j < c.params.n - 1) - pri(", "); + epri(", "); } - pri(") "); + epri(") "); pritoktree(c.body); - pri("\n"); + epri("\n"); } - pri("}\n"); + epri("}\n"); break; } } diff --git a/bootstrap/parse.c b/bootstrap/parse.c index e473f23..822212e 100644 --- a/bootstrap/parse.c +++ b/bootstrap/parse.c @@ -195,6 +195,7 @@ readnumber(struct tok *res, const char *s) { else if (!strcasecmp(suffix, "f")) res->flit.ty = ty_f32; else if (!strcasecmp(suffix, "f32")) res->flit.ty = ty_f32; else if (!strcasecmp(suffix, "f64")) res->flit.ty = ty_f64; + else return 0; } else { res->t = TKintlit; res->ilit.i = acc; @@ -213,6 +214,7 @@ readnumber(struct tok *res, const char *s) { else if (!strcasecmp(suffix, "zs")) res->ilit.ty = ty_isize; else if (!strcasecmp(suffix, "p")) res->ilit.ty = ty_uptrint; else if (!strcasecmp(suffix, "ps")) res->ilit.ty = ty_iptrint; + else return 0; } return 1; } @@ -320,7 +322,7 @@ lex(struct parser *P) { if (readtilsep(P, s, sizeof s, 1) < 0) fatal(P, P->tokspan, "number literal too long"); if (!readnumber(&tok, s)) - fatal(P, P->tokspan, "invalid number literal"); + fatal(P, P->tokspan, "invalid number literal %q", s); tok.span = P->tokspan; return tok; } else if (aisalpha(c) || c == '_' || c == '#' || c == '$') { @@ -485,8 +487,8 @@ static struct tok lexexpects(struct parser *P, int t, const char *what) { struct tok tok; if (!lexmatch(P, &tok, t)) - fatal(P, tok.span, "expected %s (near %s)", - what ? what : tokt2str(t), tok2str(tok)); + fatal(P, tok.span, "expected %s (near %T)", + what ? what : tokt2str(t), tok); return tok; } #define lexexpect(P, t) lexexpects(P, t, NULL) @@ -548,7 +550,7 @@ parsetype(struct parser *P) { } child = parsetype(P); if (!completetype(child)) - fatal(P, tok.span, "array of incomplete type"); + fatal(P, tok.span, "array of incomplete type (%t)", child); return interntype((struct type) { TYarr, length * child->size, child->align, .child = child, .length = length @@ -556,13 +558,15 @@ parsetype(struct parser *P) { } else if (lexmatch(P, &tok, TKkw_const)) { return constify(parsetype(P)); } else if (lexmatch(P, &tok, TKkw_typeof)) { - const struct type *ty = NULL, *ty2; + const struct type *ty = NULL, *ty2, *ty0; lexexpect(P, '('); do { ty2 = parseexpr(P).ty; + ty0 = ty; ty = ty ? typeof2(ty, ty2) : ty2; if (!ty) - fatal(P, tok.span, "incompatible types in typeof(...)"); + fatal(P, tok.span, "incompatible types in typeof(...): %t and %t", + ty0, ty2); if (!lexmatch(P, &tok, ',')) { lexexpect(P, ')'); break; @@ -572,12 +576,12 @@ parsetype(struct parser *P) { } else if (lexmatch(P, &tok, TKident)) { const struct decl *decl = finddecl(P, tok.str); if (!decl) - fatal(P, P->tokspan, "%s is not defined", tok2str(tok)); + fatal(P, P->tokspan, "%T is not defined", tok); if (decl->t != Dtype) - fatal(P, P->tokspan, "%s is not a type", tok2str(tok)); + fatal(P, P->tokspan, "%T is not a type", tok); return decl->ty; } - fatal(P, P->tokspan, "expected type (near %s)", tok2str(tok)); + fatal(P, P->tokspan, "expected type (near %T)", tok); } static const struct type * @@ -662,7 +666,7 @@ parsestructini(struct parser *P, const struct type *ty) { lexexpect(P, ':'); idx = structfldnam2idx(ty, fnam); if (idx < 0) - fatal(P, tok.span, "struct has no field `%s'", fnam); + fatal(P, tok.span, "struct %t has no field `%s'", ty, fnam); } fld = structidx2fld(ty, idx++); @@ -672,7 +676,9 @@ parsestructini(struct parser *P, const struct type *ty) { 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"); + fatal(P, e.span, + "incompatible element `%s` type in struct initializer (%t, expected %t)", + fld->name, e.ty, fld->ty); vec_push(&args, ((struct iniarg) { .fld = fld->name, @@ -717,7 +723,9 @@ parsearrini(struct parser *P, const struct type *ty) { P->targty = ty->child; e = parseexpr(P); if (!typeof2(ty->child, e.ty)) - fatal(P, e.span, "incompatible element type in array initializer"); + fatal(P, e.span, + "incompatible element type in array initializer (%t, expected %t)", + e.ty, ty->child); if (ty->length >= 0 && iota >= ty->length) fatal(P, e.span, "excess elements in array initializer"); @@ -780,7 +788,7 @@ pexprimary(struct parser *P) { decl = finddecl(P, tok.str); if (!decl) - fatal(P, tok.span, "%s is not defined", tok2str(tok)); + fatal(P, tok.span, "%T is not defined", tok); if (decl->t == Dtype) { const struct type *ty = decl->ty; if (ty->t == TYenum) { @@ -840,8 +848,7 @@ pexprimary(struct parser *P) { for (i = 0; i < ex.ty->enu.vals.n; ++i) if (!strcmp(ex.ty->enu.vals.d[i].name, vname)) goto found; - fatal(P, tok.span, "enum `%s' contains no variant `%s'", - ex.ty->enu.name, vname); + fatal(P, tok.span, "enum %t contains no variant `%s'", ex.ty, vname); found: P->used_targty = 1; ex.t = Eenumval; @@ -879,17 +886,19 @@ pexpostfix(struct parser *P) { int i = 0, n = ex.ty->fn.params.n; if (ex.ty->t != TYfn) - fatal(P, ex.span, "callee is not a function"); + fatal(P, ex.span, "callee is not a function (is %t)", ex.ty); while (!lexmatch(P, NULL, ')')) { struct expr arg; if (i == n && ! ex.ty->fn.variadic) - fatal(P, arg.span, "too many args for call"); + fatal(P, arg.span, "too many args for call: (expected %d)", n); if (i < n) P->targty = ex.ty->fn.params.d[i]; arg = parseexpr(P); if (i < n && !typeof2(arg.ty, ex.ty->fn.params.d[i++])) - fatal(P, arg.span, "call argument #%d type mismatch", i); + fatal(P, arg.span, + "call argument #%d type mismatch (%t, expected %t)", + i, arg.ty, ex.ty->fn.params.d[i - 1]); vec_push(&args, arg); if (!lexmatch(P, NULL, ',')) { @@ -907,24 +916,26 @@ pexpostfix(struct parser *P) { rhs = parseexpr(P); lexexpect(P, ']'); if (lhs.ty->t != TYarr && lhs.ty->t != TYptr) - fatal(P, lhs.span, "indexee is not array or pointer type"); + fatal(P, lhs.span, "indexee is not array or pointer type (%t)", + lhs.ty); if (rhs.ty->t != TYint) - fatal(P, lhs.span, "index expression type is not integral"); + fatal(P, lhs.span, "index expression type is not integral (%t)", + rhs.ty); ex.t = Eindex; ex.span = tok.span; ex.ty = lhs.ty->child; ex.index.lhs = exprdup(lhs); ex.index.rhs = exprdup(rhs); } else if (lexmatch(P, &tok, '++') || lexmatch(P, &tok, '--')) { - if (!isnumtype(ex.ty)) - fatal(P, ex.span, "invalid operand to unary operator %s: not numeric", - tok2str(tok)); + if (!isnumtype(ex.ty) && ex.ty->t != TYptr) + fatal(P, ex.span, "invalid %t operand to postfix operator %T: not numeric", + ex.ty, tok); if (!islvalue(&ex)) - fatal(P, ex.span, "left operand to postfix %s operator is not lvalue", - tok2str(tok)); + fatal(P, ex.span, "left operand to postfix %T operator is not lvalue", + tok); if (ex.ty->konst) - fatal(P, ex.span, "left operand to postfix %s operator is const", - tok2str(tok)); + fatal(P, ex.span, "left operand to postfix %T operator is const", + tok); ex = (struct expr) { Epostfix, tok.span, ex.ty, .unop = { tok.t, exprdup(ex) @@ -936,7 +947,7 @@ pexpostfix(struct parser *P) { int idx = structfldnam2idx(ex.ty, fnam); struct aggfield *fld = &ex.ty->agg.flds.d[idx]; if (idx < 0) - fatal(P, tok.span, "no such field `%s'", fnam); + fatal(P, tok.span, "%t has no such field `%s'", ex.ty, fnam); ex.get.lhs = exprdup(ex); ex.t = Eget; @@ -944,8 +955,8 @@ pexpostfix(struct parser *P) { ex.ty = fld->ty; ex.get.fld = fnam; } else { - fatal(P, tok.span, "cannot acces `%s': left-hand-side is not an aggregate", - fnam); + fatal(P, tok.span, "cannot acces `%s': left-hand-side is not an aggregate (%t)", + fnam, ex.ty); } } else { break; @@ -963,11 +974,11 @@ pexprefix(struct parser *P) { const struct type *ty = numpromote(ex.ty); if (!ty) - fatal(P, ex.span, "invalid operand to unary operator %s: not numeric", - tok2str(tok)); + fatal(P, ex.span, "invalid %t operand to unary operator %T: not numeric", + ex.ty, tok); if (ty->t != TYint && tok.t == '~') - fatal(P, ex.span, "invalid operand to unary operator %s: not integral", - tok2str(tok)); + fatal(P, ex.span, "invalid %t operand to unary operator %T: not integral", + ty, tok); return (struct expr) { Eprefix, tok.span, ty, .unop = { tok.t, exprdup(ex) @@ -976,15 +987,15 @@ pexprefix(struct parser *P) { } else if (lexmatch(P, &tok, '++') || lexmatch(P, &tok, '--')) { struct expr ex = pexprefix(P); - if (!isnumtype(ex.ty)) - fatal(P, ex.span, "invalid operand to unary operator %s: not numeric", - tok2str(tok)); + if (!isnumtype(ex.ty) && ex.ty->t != TYptr) + fatal(P, ex.span, "invalid %t operand to unary operator %T: not numeric", + ex.ty, tok); if (!islvalue(&ex)) - fatal(P, ex.span, "left operand to prefix %s operator is not lvalue", - tok2str(tok)); + fatal(P, ex.span, "left operand to prefix %T operator is not lvalue", + ex.ty, tok); if (ex.ty->konst) - fatal(P, ex.span, "left operand to prefix %s operator is const", - tok2str(tok)); + fatal(P, ex.span, "left operand to prefix %T operator is const", + ex.ty, tok); return (struct expr) { Eprefix, tok.span, ex.ty, .unop = { tok.t, exprdup(ex) @@ -994,8 +1005,8 @@ pexprefix(struct parser *P) { struct expr ex = pexprefix(P); if (ex.ty->t != TYbool) - fatal(P, ex.span, "invalid operand to unary operator %s: not boolean", - tok2str(tok)); + fatal(P, ex.span, "invalid %t operand to unary operator %T: not boolean", + ex.ty, tok); return (struct expr) { Eprefix, tok.span, ty_bool, .unop = { 'not', exprdup(ex) @@ -1004,9 +1015,9 @@ pexprefix(struct parser *P) { } else if (lexmatch(P, &tok, '*')) { struct expr ex = pexprefix(P); if (ex.ty->t != TYptr) - fatal(P, ex.span, "invalid operand type to dereference, not pointer"); + fatal(P, ex.span, "invalid %t operand to dereference, not pointer", ex.ty); if (!completetype(ex.ty->child) && ex.ty->child->t != TYfn) - fatal(P, ex.span, "invalid operand type to dereference, incomplete"); + fatal(P, ex.span, "invalid %t operand to dereference, incomplete", ex.ty); return (struct expr) { Eprefix, tok.span, ex.ty->child, .unop = { '*', exprdup(ex) @@ -1016,7 +1027,7 @@ pexprefix(struct parser *P) { struct expr ex = pexprefix(P); struct type ty2 = { TYptr, g_targ.ptrsize, .child = ex.ty}; if (!islvalue(&ex) && !(ex.t == Ename && ex.ref->t == Dfn)) - fatal(P, ex.span, "invalid operand type to address-of, not an lvalue"); + fatal(P, ex.span, "invalid operand to `&': not an lvalue"); return (struct expr) { Eprefix, tok.span, interntype(ty2), .unop = { '&', exprdup(ex) @@ -1040,7 +1051,7 @@ pexprefix(struct parser *P) { else if (from->t == TYenum && to->t == TYint) ; else if (from->t == TYint && to->t == TYenum) ; else - fatal(P, tok.span, "invalid cast"); // TODO better diagnostics... + fatal(P, tok.span, "invalid cast from %t to %t", from, to); P->targty = to; return (struct expr) { @@ -1098,12 +1109,13 @@ pexbitarith(struct parser *P) { ty = ty_isize; } else if (tokt != '##' && !isnumtype(ty)) { err: - fatal(P, tok.span, "invalid operands to binary operator %s", - tokt2str(tokt)); + fatal(P, tok.span, "invalid operands %t and %t to binary operator %k", + ex.ty, rhs.ty, tokt); } if (oret == 2 && ty->t == TYfloat) - fatal(P, tok.span, "invalid operands to bitwise operator: not integral", - tokt2str(tokt)); + fatal(P, tok.span, + "invalid operands %t and %t to bitwise operator %k: not integral", + ex.ty, rhs.ty, tokt); if (tokt != '##') { ex = (struct expr) { Ebinop, tok.span, ty, .binop = { @@ -1157,11 +1169,12 @@ pexcmp(struct parser *P) { if (matchcmpop(P, &tok)) { struct expr rhs = pexbitarith(P); if (!typeof2(ex.ty, rhs.ty)) - fatal(P, tok.span, "incompatible operands to binary operator %s", - tok2str(tok)); + fatal(P, tok.span, "incompatible operands %t and %t to binary operator %T", + ex.ty, rhs.ty, tok); if (tok.t != '==' && !isnumtype(typeof2(ex.ty, rhs.ty))) - fatal(P, tok.span, "invalid operands to relational operator %s: not numeric", - tok2str(tok)); + fatal(P, tok.span, + "invalid operands %t and %t to relational operator %T: not numeric", + ex.ty, rhs.ty, tok); ex = (struct expr) { Ebinop, tok.span, ty_bool, .binop = { tok.t, exprdup(ex), exprdup(rhs) @@ -1187,8 +1200,9 @@ pexlog(struct parser *P) { while (lexmatch(P, &tok, tokt)) { struct expr rhs = pexcmp(P); if (ex.ty->t != TYbool || rhs.ty->t != TYbool) - fatal(P, tok.span, "invalid operands to binary operator %s: not boolean", - tokt2str(tok.t)); + fatal(P, tok.span, + "invalid operands %t and %t to binary operator %k", + ex.ty, rhs.ty, tok.t); ex = (struct expr) { Ebinop, tok.span, ty_bool, .binop = { tokt == TKkw_and ? 'and' : 'or', exprdup(ex), exprdup(rhs) @@ -1212,10 +1226,15 @@ pexcond(struct parser *P) { struct expr ex3; const struct type *ty; + if (ex.ty->t != TYbool) + fatal(P, ex.span, "invalid test operand %t to conditional operator", ex.ty); + lexexpect(P, ':'); ex3 = pexcond(P); if (!(ty = typeof2(ex2.ty, ex3.ty))) - fatal(P, tok.span, "conditional operator branches have incompatible types"); + fatal(P, tok.span, + "conditional operator branches have incompatible types %t and %t", + ex2.ty, ex3.ty); ex = (struct expr) { Econd, tok.span, ty, .cond = { exprdup(ex), exprdup(ex2), exprdup(ex3) @@ -1256,16 +1275,18 @@ pexassign(struct parser *P) { struct expr rhs = pexcond(P); if (!islvalue(&ex)) fatal(P, ex.span, - "left operand to assignment operator %s is not an lvalue", - tok2str(tok)); - if (!typeof2(ex.ty, rhs.ty)) + "left operand to assignment operator %T is not an lvalue", + tok); + if (!typeof2(ex.ty, rhs.ty) + && !(ex.ty->t == TYptr && rhs.ty->t == TYint + && (tok.t == '+=' || tok.t == '-='))) fatal(P, ex.span, - "operands to assignment operator %s have incompatible types", - tok2str(tok)); + "operands %t and %t to assignment operator %T have incompatible types", + ex.ty, rhs.ty, tok); if (ex.ty->konst) fatal(P, ex.span, - "left operand to assignment operator %s is const", - tok2str(tok)); + "left operand to assignment operator %T is const", + tok); ex = (struct expr) { Ebinop, tok.span, ex.ty, .binop = { tok.t, exprdup(ex), exprdup(rhs) @@ -1311,7 +1332,7 @@ parsevardecl(struct parser *P, struct decl *decl) { P->targty = ty; ini = exprdup(parseexpr(P)); } else if (decl->t == Dlet) { - fatal(P, tok.span, "variable must be initialized"); + fatal(P, tok.span, "variable %T must be initialized", tok); } } @@ -1320,10 +1341,12 @@ parsevardecl(struct parser *P, struct decl *decl) { // fatal(P, ini->span, "static initializer isn't constant"); if (ini && !typeof2(ty, ini->ty)) - fatal(P, tok.span, "incompatible initializer type"); + fatal(P, tok.span, "incompatible initializer type (%t, expected %t)", + ini->ty, ty); if (!completetype(ty)) - fatal(P, tok.span, "let `%s': variable type is incomplete", name); + fatal(P, tok.span, "let `%s': variable type %t is incomplete", + name, ty); if (konst) ty = constify(ty); @@ -1351,7 +1374,8 @@ pstifelse(struct parser *P) { st.ifelse.test = parseexpr(P); if (st.ifelse.test.ty->t != TYbool && st.ifelse.test.ty->t != TYptr) fatal(P, st.ifelse.test.span, - "if statement condition must be bool or pointer"); + "if statement condition must be bool or pointer (%t)", + st.ifelse.test.ty); lexexpect(P, '{'); st.ifelse.t = parseblock(P).block; if (lexmatch(P, NULL, TKkw_else)) { @@ -1393,7 +1417,8 @@ pstfor(struct parser *P) { if (st.loop.test.ty->t != TYbool && st.loop.test.ty->t != TYptr) fatal(P, st.loop.test.span, - "for statement condition must be bool or pointer"); + "for statement condition must be bool or pointer (%t)", + st.loop.test.ty); if (!lexmatch(P, &tok, '{')) { st.loop.next = exprdup(parseexpr(P)); @@ -1432,7 +1457,8 @@ pstiswitch(struct parser *P, const struct expr *test) { if (!fold(&ex)) fatal(P, ex.span, "case expression is not constant"); if (!typeof2(ex.ty, test->ty)) - fatal(P, ex.span, "case expression has incorrect type"); + fatal(P, ex.span, "case expression has incorrect type (%t, expected %t)", + ex.ty, test->ty); vec_push(&es, ex); if (!lexmatch(P, &tok, ',')) { lexexpect(P, ';'); @@ -1531,7 +1557,7 @@ parseexpandmacro(struct parser *P, const struct macro *macro) { goto arg_done; break; case TKeof: - fatal(P, espan, "unterminated macro invokation"); + fatal(P, espan, "unterminated macro `%s' invokation", macro->name); } vec_push(&toks, tok); } @@ -1598,7 +1624,8 @@ parsestmt(struct parser *P) { st.loop.test = parseexpr(P); if (st.loop.test.ty->t != TYbool && st.loop.test.ty->t != TYptr) fatal(P, st.loop.test.span, - "while statement condition must be bool or pointer"); + "while statement condition must be bool or pointer (%t)", + st.loop.test.ty); lexexpect(P, '{'); st.loop.body = parseblock(P).block; } else if (lexmatch(P, &tok, TKkw_for)) { @@ -1618,7 +1645,8 @@ parsestmt(struct parser *P) { lexexpect(P, ';'); if (!typeof2(st.retex->ty, P->curfn->retty)) fatal(P, st.retex->span, - "incompatible type in return statement"); + "incompatible type in return statement (%t, expected %t)", + st.retex->ty, P->curfn->retty); } else if (P->curfn->retty != ty_void) { fatal(P, tok.span, "return statement in non-void function must return a value"); @@ -1726,7 +1754,8 @@ parsefn(struct decl *decl, struct parser *P) { vec_slice_cpy(&fn->params, ¶ms); fn->retty = unconstify(parsetype(P)); if (fn->retty != ty_void && !completetype(fn->retty)) { - fatal(P, tok.span, "return type is incomplette"); + fatal(P, tok.span, "return type is incomplette (%t)", + fn->retty); } fn->selfty = fntype(fn); if (!lexmatch(P, &tok, ';')) { @@ -1880,7 +1909,8 @@ parseenum(struct parser *P, const char *name) { if (lexmatch(P, &tok, ':')) { ty.enu.intty = unconstify(parsetype(P)); if (ty.enu.intty->t != TYint) - fatal(P, tok.span, "enum backing type is not integral"); + fatal(P, tok.span, "enum backing type is not integral (%t)", + ty.enu.intty); } lexexpect(P, '{'); @@ -1947,8 +1977,14 @@ parseagg(struct parser *P, const char *name, int kind) { const struct type *ty = parsetype(P); size_t off = kind == TYunion ? 0 : ALIGNUP(size, ty->align); + int i; struct aggfield fld; + vec_foreach(&flds, fld, i) + if (!strcmp(fnam, fld.name)) + fatal(P, tok.span, "duplicate field %T", tok); + if (!completetype(ty)) - fatal(P, tok.span, "aggregate field `%s' is of incomplete type", fnam); + fatal(P, tok.span, "aggregate field `%s' is of incomplete type (%t)", + fnam, ty); align = MAX(align, ty->align); if (kind == TYstruct) diff --git a/bootstrap/test.cff b/bootstrap/test.cff index f6deb67..966176f 100644 --- a/bootstrap/test.cff +++ b/bootstrap/test.cff @@ -17,7 +17,7 @@ enum Color { static xs *void = {}; -extern fn main (argc int, argv **u8) void { +extern fn main (argc int, argv **u8) int { extern fn printf(fmt *const u8, ...) int; let colors [3]Color = { :Red, :Green, :Blue } ; @@ -29,5 +29,5 @@ extern fn main (argc int, argv **u8) void { printf("%d\n", is[i]); } - return; + return 0; } diff --git a/bootstrap/util.c b/bootstrap/util.c index 855091a..bb38b90 100644 --- a/bootstrap/util.c +++ b/bootstrap/util.c @@ -87,17 +87,17 @@ fatal(struct parser *P, struct span span, const char *fmt, ...) { va_start(ap, fmt); int i = 0; - fprintf(stderr, "%s:%d:%d: error: ", fileid2path(span.fileid), span.line, span.col); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); + epri("%s:%d:%d: error: ", fileid2path(span.fileid), span.line, span.col); + vepri(fmt, ap); + epri("\n"); for (struct expan *ep = P->curexpan; ep; ep = ep->prev, ++i) { if (ep->name && (i < 8 || !ep->prev || !ep->prev->prev)) { span = ep->span; - fprintf(stderr, " while expanding macro `%s' at %s:%d:%d\n", + epri(" while expanding macro `%s' at %s:%d:%d\n", ep->name, fileid2path(ep->span.fileid), span.line, span.col); } else if (ep->name && i == 10) { - fprintf(stderr, " ... (some expansions omitted)\n"); + epri(" ... (some expansions omitted)\n"); } } va_end(ap); |