From d4c768a481b923d407201c25d3d750040b6ccd44 Mon Sep 17 00:00:00 2001 From: WormHeamer Date: Sun, 10 Aug 2025 02:46:50 -0400 Subject: add type-specifiers to let, N_UNINIT for uninitialized values --- ir.c | 20 +++++++++++++++-- ir.h | 4 +++- main.c | 73 +++++++++++++++++++++++++++++++++++++------------------------- peephole.c | 5 +---- test.lang | 3 ++- 5 files changed, 68 insertions(+), 37 deletions(-) diff --git a/ir.c b/ir.c index 6e91faa..125e882 100644 --- a/ir.c +++ b/ir.c @@ -122,7 +122,6 @@ Node *node_new_empty(Proc *p, NodeType t) { return n; } -int type_check(Node *); Node *node_newv(Proc *p, NodeType t, Node *ctrl, ...) { Node *node = node_new_empty(p, t); va_list ap; @@ -317,7 +316,13 @@ void type_err(Node *n, Lexer *l) { lex_error_at(l, n->src_pos, LE_ERROR, str_fmt(&l->arena, "type error %s (%S)", node_type_name(n->op), s)); } -int type_check(Node *n) { +void type_expected(Type *want, Node *n, Lexer *l) { + if (type_base_eql(want, &n->type)) return; + lex_error_at(l, n->src_pos, LE_ERROR, str_fmt(&l->arena, "type error: expected %S, but got %S", + type_desc(want, &l->arena), type_desc(&n->type, &l->arena))); +} + +static int type_ok(Node *n) { switch (n->op) { case N_PHI: n->type = (Type) { .lvl = T_TOP, .t = IN(n, 1)->type.t }; @@ -355,3 +360,14 @@ int type_check(Node *n) { return 1; } } + +void type_check(Node *n, Lexer *l) { + for (int i = 0; i < n->in.len; i++) { + if (IN(n,i) && IN(n,i)->op == N_UNINIT) { + lex_error_at(l, n->src_pos, LE_ERROR, + str_fmt(&l->arena, "attempt to use uninitialized %S value", + type_desc(&IN(n,i)->type, &l->arena))); + } + } + if (!type_ok(n)) type_err(n, l); +} diff --git a/ir.h b/ir.h index ca3e8b4..0ae71c8 100644 --- a/ir.h +++ b/ir.h @@ -43,9 +43,10 @@ struct Node; int type_eql(Type *a, Type *b); int type_base_eql(Type *a, Type *b); int value_eql(Value *a, Value *b); -int type_check(struct Node *n); +void type_check(struct Node *n, Lexer *l); Str type_desc(Type *t, Arena *arena); void type_err(struct Node *n, Lexer *l); +void type_expected(Type *want, struct Node *n, Lexer *l); /* nodes */ @@ -61,6 +62,7 @@ void type_err(struct Node *n, Lexer *l); X(RETURN, "return")\ X(KEEPALIVE, "keepalive")\ X(LIT, "literal")\ + X(UNINIT, "uninitialized value")\ X(OP_ADD, "add")\ X(OP_SUB, "sub")\ X(OP_MUL, "mul")\ diff --git a/main.c b/main.c index 9fba001..ae19e7a 100644 --- a/main.c +++ b/main.c @@ -75,14 +75,50 @@ void parse_return(Lexer *l, Proc *p) { ctrl(p, NULL); } +Type parse_type(Lexer *l, Proc *proc) { + (void)proc; + Type t = { .lvl = T_BOT }; + if (l->tok == TOK_DEREF) { + lex_next(l); + t.t = T_PTR; + t.next = new(&proc->arena, Type); + *t.next = parse_type(l, proc); + return t; + } + lex_expected(l, TM_IDENT); + if (l->tok == TOK_IDENT) { + if (str_eql(l->ident, S("i64"))) { + t.t = T_INT; + } else if (str_eql(l->ident, S("bool"))) { + t.t = T_BOOL; + } else { + lex_error(l, LE_ERROR, S("unknown type")); + } + } + lex_next(l); + return t; +} + void parse_let(Lexer *l, Proc *p) { recurse: lex_expect(l, TM_IDENT); Str name = l->ident; LexSpan pos = l->pos; - lex_expect(l, TM_EQL); lex_next(l); - Node *rhs = parse_expr(l, p); + Node *rhs = NULL; + Type t = { .t = T_NONE }; + if (l->tok != TOK_EQL) { + t = parse_type(l, p); + if (l->tok != TOK_EQL) { + rhs = node_new(p, N_UNINIT, p->start); + rhs->type = t; + } + } + if (l->tok == TOK_EQL) { + lex_next(l); + rhs = parse_expr(l, p); + if (t.t != T_NONE) type_expected(&t, rhs, l); + } NameBinding *b = scope_bind(&p->scope, name, rhs, pos, p); if (b) { lex_error_at(l, pos, LE_WARN, S("shadowing previous declaration")); @@ -125,6 +161,11 @@ void parse_assign(Lexer *l, Proc *p) { type_desc(&e->type, &p->arena), type_desc(&b->node->type, &p->arena))); } + if (e->op == N_UNINIT) { + lex_error_at(l, e->src_pos, LE_ERROR, + str_fmt(&p->arena, "assigning from uninitialized %S value", + type_desc(&e->type, &p->arena))); + } scope_update(b, e, p); } @@ -317,29 +358,6 @@ void parse_stmt(Lexer *l, Proc *p) { } } -Type parse_type(Lexer *l, Proc *proc) { - (void)proc; - Type t = { .lvl = T_BOT }; - if (l->tok == TOK_DEREF) { - lex_next(l); - t.t = T_PTR; - t.next = new(&proc->arena, Type); - *t.next = parse_type(l, proc); - return t; - } - if (l->tok == TOK_IDENT) { - if (str_eql(l->ident, S("i64"))) { - t.t = T_INT; - } else if (str_eql(l->ident, S("bool"))) { - t.t = T_BOOL; - } - } else { - lex_error(l, LE_ERROR, S("unknown type")); - } - lex_next(l); - return t; -} - void parse_args_list(Lexer *l, Proc *proc) { Node *start = proc->start; int i = 0; @@ -390,10 +408,7 @@ void proc_opt_fwd(Proc *p, Lexer *l, Node *n) { switch (n->op) { case N_START: for (int i = 0; i < n->out.len; i++) { - if (!(NMASK(n->out.data[i]->op) & (NM_LIT | NM_PROJ))) { - proc_opt_fwd(p, l, n->out.data[i]); - break; - } + proc_opt_fwd(p, l, n->out.data[i]); } break; case N_IF_ELSE: diff --git a/peephole.c b/peephole.c index 58a7612..b8118db 100644 --- a/peephole.c +++ b/peephole.c @@ -250,10 +250,7 @@ static inline int is_zero(Node *n) { /* needs lexer for error reporting */ Node *node_idealize(Node *n, Proc *p, Lexer *l) { - if (!type_check(n)) { - type_err(n, l); - } - + type_check(n, l); if (no_opt) return NULL; /* try to compute a literal value */ diff --git a/test.lang b/test.lang index 24a931d..2de85c1 100644 --- a/test.lang +++ b/test.lang @@ -1,8 +1,9 @@ func main(a, b i64) i64 { + let x i64, y bool if a = b { let t = a a := b b := t } - return a + b + return a } -- cgit v1.2.3