aboutsummaryrefslogtreecommitdiff
path: root/bootstrap
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-05 05:35:57 +0200
committerlemon <lsof@mailbox.org>2022-08-05 05:35:57 +0200
commitfc29f3f2b0c3a7c5ef1c75d910cf0815d2edbba2 (patch)
treeeef251348f3e1b3a30a3a6296fa05e36971f3140 /bootstrap
parentb0d95956fcade40a2d608ccea79e2e989f97b72f (diff)
better diagnostics
Diffstat (limited to 'bootstrap')
-rw-r--r--bootstrap/all.h2
-rw-r--r--bootstrap/dump.c177
-rw-r--r--bootstrap/parse.c190
-rw-r--r--bootstrap/test.cff4
-rw-r--r--bootstrap/util.c10
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, &params);
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);