From fc29f3f2b0c3a7c5ef1c75d910cf0815d2edbba2 Mon Sep 17 00:00:00 2001 From: lemon Date: Fri, 5 Aug 2022 05:35:57 +0200 Subject: better diagnostics --- bootstrap/parse.c | 190 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 113 insertions(+), 77 deletions(-) (limited to 'bootstrap/parse.c') 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) -- cgit v1.2.3