diff options
| author | WormHeamer | 2025-08-10 05:20:40 -0400 |
|---|---|---|
| committer | WormHeamer | 2025-08-10 05:20:40 -0400 |
| commit | a9810c2281a2c4fc2265aa8c59fa9bf101e3a3b5 (patch) | |
| tree | a9afaee0d6514505ccd35343bca8bb931a196f76 | |
| parent | c9980711ce42de9cf58db35f41ce1ac42bfea0c7 (diff) | |
better error reporting around uninitialized values
| -rw-r--r-- | ir.c | 40 | ||||
| -rw-r--r-- | main.c | 39 | ||||
| -rw-r--r-- | test.lang | 7 |
3 files changed, 34 insertions, 52 deletions
@@ -359,19 +359,9 @@ static int type_ok(Node *n) { } } -/* TODO: make it so - * func foo(a, b i64) i64 { - * let x i64 - * if a < b { - * return 0 - * } else { - * x := 3 - * } - * return x - * } - * doesn't throw warnings (e.g. don't generate phi nodes if one scope is - * guaranteed to return early) - */ +void type_check(Node *n, Lexer *l) { + if (!type_ok(n)) type_err(n, l); +} int node_uninit(Node *n) { return n->op == N_UNINIT; @@ -386,27 +376,3 @@ int node_maybe_uninit(Node *n) { } return 0; } - -void uninit_check(Node *n, Lexer *l) { - if (NMASK(n->op) & ~NM_PHI) { - for (int i = 0; i < n->in.len; i++) { - Node *o = IN(n, i); - if (!o) continue; - if (node_uninit(o)) { - fprintf(stderr, "%s\n", node_type_name(n->op)); - lex_error_at(l, o->src_pos, LE_WARN, - str_fmt(&l->arena, "uninitialized %S", - type_desc(&IN(n,i)->type, &l->arena))); - } else if (node_maybe_uninit(o)) { - lex_error_at(l, o->src_pos, LE_WARN, - str_fmt(&l->arena, "possibly uninitialized %S", - type_desc(&o->type, &l->arena))); - } - } - } -} - -void type_check(Node *n, Lexer *l) { - uninit_check(n, l); - if (!type_ok(n)) type_err(n, l); -} @@ -32,7 +32,7 @@ void unit_free(Unit *u) { /* parsing */ -Node *parse_expr(Lexer *l, Proc *p); +Node *parse_expr(Lexer *l, Proc *p, Type *twant); /* TODO: eliminate unused if-else statements at the end of compilation * they don't get pruned out by peephole optimizations if there are phi @@ -58,7 +58,7 @@ void parse_return(Lexer *l, Proc *p) { if (p->ret_type.t == T_NONE) { n = node_new(p, N_RETURN, p->ctrl); } else { - Node *e = parse_expr(l, p); + Node *e = parse_expr(l, p, NULL); if (!type_base_eql(&e->type, &p->ret_type)) { lex_error_at(l, e->src_pos, LE_ERROR, str_fmt(&p->arena, "incorrect return type (expected %S, got %S)", @@ -116,7 +116,7 @@ recurse: } if (l->tok == TOK_EQL) { lex_next(l); - rhs = parse_expr(l, p); + rhs = parse_expr(l, p, t.t == T_NONE ? NULL : &t); if (t.t != T_NONE) type_expected(&t, rhs, l); } NameBinding *b = scope_bind(&p->scope, name, rhs, pos, p); @@ -154,7 +154,7 @@ void parse_assign(Lexer *l, Proc *p) { if (!b) { lex_error_at(l, pos, LE_ERROR, S("undeclared identifier")); } - Node *e = parse_expr(l, p); + Node *e = parse_expr(l, p, &b->node->type); if (!type_base_eql(&e->type, &b->node->type)) { lex_error_at(l, pos, LE_ERROR, str_fmt(&p->arena, "tried to assign value of type %S to variable of type %S", @@ -256,7 +256,7 @@ void merge_scope(Lexer *l, Proc *p, Node *region, ScopeNameList *before, ScopeNa void parse_if(Lexer *l, Proc *p) { lex_next(l); - Node *cond = parse_expr(l, p); + Node *cond = parse_expr(l, p, &(Type) { .t = T_BOOL }); Node *ctrl_if = NULL, *ctrl_else = NULL; Node *if_node = node_new(p, N_IF_ELSE, p->ctrl, cond); if_node->val = (Value) { @@ -417,7 +417,7 @@ void proc_opt_fwd(Proc *p, Lexer *l, Node *n) { break; case N_IF_ELSE: if (n->out.len < 2) { - lex_error_at(l, n->src_pos, LE_ERROR, S("not all codepaths return")); + //lex_error_at(l, n->src_pos, LE_ERROR, S("not all codepaths return")); } for (int i = 0; i < n->out.len; i++) { Node *r = find_return(n->out.data[i]); @@ -503,13 +503,26 @@ Proc *parse_proc(Lexer *l, Unit *u) { return proc; } -Node *parse_term(Lexer *l, Proc *p) { +void uninit_check(Lexer *l, Proc *p, Node *n, LexSpan pos) { + if (node_uninit(n)) { + lex_error_at(l, pos, LE_ERROR, + str_fmt(&p->arena, "uninitialized %S", + type_desc(&n->type, &p->arena))); + } else if (node_maybe_uninit(n)) { + lex_error_at(l, pos, LE_WARN, + str_fmt(&p->arena, "possibly uninitialized %S", + type_desc(&n->type, &p->arena))); + } +} + +Node *parse_term(Lexer *l, Proc *p, Type *twant) { + (void)twant; /* to be used for .ENUM_TYPE and stuff */ Node *node = NULL; NodeType op_after = N_START; if (TMASK(l->tok) & (TM_MINUS | TM_PLUS | TM_NOT)) { Token t = l->tok; lex_next(l); - node = parse_term(l, p); + node = parse_term(l, p, twant); NodeType post_op = N_START; switch (t) { case TOK_MINUS: post_op = N_OP_NEG; break; @@ -521,7 +534,7 @@ Node *parse_term(Lexer *l, Proc *p) { } if (l->tok == TOK_LPAREN) { lex_next(l); - node = parse_expr(l, p); + node = parse_expr(l, p, NULL); lex_expected(l, TM_RPAREN); lex_next(l); node->src_pos.ofs--; @@ -533,6 +546,7 @@ Node *parse_term(Lexer *l, Proc *p) { } else { lex_error(l, LE_ERROR, S("undeclared identifier")); } + uninit_check(l, p, node, l->pos); lex_next(l); } else if (TMASK(l->tok) & (TM_TRUE | TM_FALSE)) { node = node_new_lit_bool(p, l->tok == TOK_TRUE); @@ -580,9 +594,9 @@ NodeType tok_to_bin_op(Token t) { } /* TODO: operator precedence would be kinda nice actually, sad to say */ -Node *parse_expr(Lexer *l, Proc *p) { +Node *parse_expr(Lexer *l, Proc *p, Type *twant) { LexSpan pos = l->pos; - Node *lhs = parse_term(l, p); + Node *lhs = parse_term(l, p, twant); NodeType nt = tok_to_bin_op(l->tok);; if (lhs->refs <= 0) lex_error(l, LE_ERROR, S("dead lhs")); assert(lhs->refs > 0); @@ -592,11 +606,12 @@ Node *parse_expr(Lexer *l, Proc *p) { * and therefore culled by peephole optimizations */ Node *rhs; NODE_KEEP(p, lhs, { - rhs = parse_expr(l, p); + rhs = parse_expr(l, p, &lhs->type); }); lhs = node_peephole(node_new(p, nt, NULL, lhs, rhs), p, l); } lhs->src_pos = (LexSpan) { pos.ofs, l->pos.ofs - pos.ofs }; + if (twant) type_expected(twant, lhs, l); return lhs; } @@ -1,11 +1,12 @@ func main(a, b i64) i64 { let x i64, y bool - if a < b { + if true { let t = a a := b b := t + x := 3 } else { - x := 2 + //x := 2 } - return (a + b) + x + return x } |
