#include #include #include #include #include #include #include "alloc.h" #include "analyzer.h" #include "common.h" #include "errors.h" #include "parser.h" #include "types.h" struct environ { idx_t_ up; struct declaration { idx_t_ astidx; } *buf; size_t len, cap; }; struct environs { struct environ *buf; size_t len, cap; }; struct typechkctx { struct type fnret; }; static struct environ *create_environments(struct ast, struct lexemes, struct environs *, idx_t_, idx_t_, arena *) __attribute__((returns_nonnull, nonnull)); static void typechkast(struct environs, struct type *, struct ast, struct lexemes) __attribute__((nonnull)); static idx_t_ typechkdecl(struct typechkctx, struct environs, struct type *, struct ast, struct lexemes, idx_t_, idx_t_) __attribute__((nonnull)); static idx_t_ typechkstmt(struct typechkctx, struct environs, struct type *, struct ast, struct lexemes, idx_t_, idx_t_) __attribute__((nonnull)); static idx_t_ typechkexpr(struct typechkctx, struct environs, struct type *, struct ast, struct lexemes, idx_t_, idx_t_) __attribute__((nonnull)); static idx_t_ typechkfn(struct typechkctx, struct environs, struct type *, struct ast, struct lexemes, idx_t_, idx_t_) __attribute__((nonnull)); static idx_t_ typechkblk(struct typechkctx, struct environs, struct type *, struct ast, struct lexemes, idx_t_, idx_t_) __attribute__((nonnull)); static const struct type *typegrab(struct ast, struct lexemes, idx_t_) __attribute__((returns_nonnull)); static bool typecompat(struct type, struct type); /* Defined in primitives.gperf */ const struct type *typelookup(const uchar *, size_t) __attribute__((nonnull)); struct type * analyzeast(struct ast ast, struct lexemes toks) { arena a = NULL; struct environs evs = {0}; struct type *types = bufalloc(NULL, ast.len, sizeof(*types)); memset(types, 0, ast.len * sizeof(*types)); create_environments(ast, toks, &evs, 0, ast.len - 1, &a); typechkast(evs, types, ast, toks); arena_free(&a); free(evs.buf); return types; } struct environ * create_environments(struct ast ast, struct lexemes toks, struct environs *evs, idx_t_ beg, idx_t_ end, arena *a) { assert(evs != NULL); if (evs->len == evs->cap) { evs->cap = evs->cap == 0 ? 8 : evs->cap * 2; evs->buf = bufalloc(evs->buf, evs->cap, sizeof(*evs->buf)); } struct environ *ev = evs->buf + evs->len++; *ev = (struct environ){.cap = 16}; ev->buf = arena_new(a, struct declaration, ev->cap); for (idx_t_ i = beg; likely(i <= end); i++) { switch (ast.kinds[i]) { case ASTDECL: if (beg != 0) break; __attribute__((fallthrough)); case ASTCDECL: { struct declaration cd = {.astidx = i}; struct strview sv = toks.strs[ast.lexemes[i]]; /* TODO: Sorted insert and existence check */ for (size_t i = 0; i < ev->len; i++) { struct strview sv2 = toks.strs[ast.lexemes[ev->buf[i].astidx]]; if (sv.len == sv2.len && memcmp(sv.p, sv2.p, sv.len) == 0) { err("analyzer: Symbol ‘%.*s’ declared multiple times", (int)sv.len, sv.p); } } if (ev->len == ev->cap) { ev->buf = arena_grow(a, ev->buf, struct declaration, ev->cap, ev->cap * 2); ev->cap *= 2; } ev->buf[ev->len++] = cd; break; } case ASTBLK: { idx_t_ lhs, rhs, up; lhs = ast.kids[i].lhs; rhs = ast.kids[i].rhs; up = ev - evs->buf; create_environments(ast, toks, evs, lhs, rhs, a)->up = up; i = rhs; break; } } } return ev; } void typechkast(struct environs evs, struct type *types, struct ast ast, struct lexemes toks) { struct typechkctx ctx = {0}; for (idx_t_ i = 0; likely(i < ast.len); i = typechkdecl(ctx, evs, types, ast, toks, 0, i)) { assert(ast.kinds[i] == ASTDECL || ast.kinds[i] == ASTCDECL); } } const struct type * typegrab(struct ast ast, struct lexemes toks, idx_t_ i) { struct strview sv = toks.strs[ast.lexemes[i]]; const struct type *tp = typelookup(sv.p, sv.len); if (tp == NULL) err("analyzer: Unknown type ‘%.*s’", (int)sv.len, sv.p); return tp; } idx_t_ typechkdecl(struct typechkctx ctx, struct environs evs, struct type *types, struct ast ast, struct lexemes toks, idx_t_ evi, idx_t_ i) { types[i].kind = TYPE_CHECKING; struct pair p = ast.kids[i]; struct type ltype, rtype; ltype.kind = TYPE_UNSET; assert(p.rhs != AST_EMPTY); if (p.lhs != AST_EMPTY) ltype = *typegrab(ast, toks, p.lhs); idx_t_ ni = typechkexpr(ctx, evs, types, ast, toks, evi, p.rhs); rtype = types[p.rhs]; if (ltype.kind == TYPE_UNSET) ltype = rtype; else if (!typecompat(ltype, rtype)) err("analyzer: Type mismatch"); types[i] = ltype; return ni; } idx_t_ typechkstmt(struct typechkctx ctx, struct environs evs, struct type *types, struct ast ast, struct lexemes toks, idx_t_ evi, idx_t_ i) { switch (ast.kinds[i]) { case ASTDECL: case ASTCDECL: return typechkdecl(ctx, evs, types, ast, toks, evi, i); case ASTRET: { idx_t_ expr = ast.kids[i].rhs; if (expr == AST_EMPTY) { if (ctx.fnret.kind != TYPE_UNSET) err("analyzer: %s: Type mismatch", __func__); return i + 1; } idx_t_ ni = typechkexpr(ctx, evs, types, ast, toks, evi, ast.kids[i].rhs); types[i] = types[ast.kids[i].rhs]; if (!typecompat(ctx.fnret, types[i])) err("analyzer: %s: Type mismatch", __func__); return ni; } } assert(!"unreachable"); __builtin_unreachable(); } idx_t_ typechkexpr(struct typechkctx ctx, struct environs evs, struct type *types, struct ast ast, struct lexemes toks, idx_t_ evi, idx_t_ i) { switch (ast.kinds[i]) { case ASTNUMLIT: types[i].kind = TYPE_NUM; types[i].size = 0; types[i].issigned = true; return i + 1; case ASTIDENT: { struct environ ev = evs.buf[evi]; struct strview sv = toks.strs[ast.lexemes[i]]; for (;;) { for (size_t j = 0; j < ev.len; j++) { struct strview sv2 = toks.strs[ast.lexemes[ev.buf[j].astidx]]; if (sv.len != sv2.len || memcmp(sv.p, sv2.p, sv.len) != 0) continue; switch (types[j].kind) { case TYPE_UNSET: typechkdecl(ctx, evs, types, ast, toks, evi, j); break; case TYPE_CHECKING: err("analyzer: Circular definition of ‘%.*s’", (int)sv.len, sv.p); } types[i] = types[j]; return i + 1; } if (evi == 0) err("analyzer: Unknown constant ‘%.*s’", (int)sv.len, sv.p); evi = ev.up; } } case ASTFN: return typechkfn(ctx, evs, types, ast, toks, evi, i); default: err("analyzer: Unexpected AST kind %u", ast.kinds[i]); __builtin_unreachable(); } } idx_t_ typechkfn(struct typechkctx ctx, struct environs evs, struct type *types, struct ast ast, struct lexemes toks, idx_t_ evi, idx_t_ i) { struct type t = {.kind = TYPE_FN}; struct pair p = ast.kids[i]; idx_t_ proto = p.lhs; if (ast.kids[proto].rhs != AST_EMPTY) { t.ret = typegrab(ast, toks, ast.kids[proto].rhs); ctx.fnret = *t.ret; } types[i] = t; idx_t_ ni = typechkblk(ctx, evs, types, ast, toks, evi, p.rhs); return ni; } idx_t_ typechkblk(struct typechkctx ctx, struct environs evs, struct type *types, struct ast ast, struct lexemes toks, idx_t_ evi, idx_t_ i) { struct pair p = ast.kids[i]; for (i = p.lhs; i <= p.rhs;) i = typechkstmt(ctx, evs, types, ast, toks, evi, i); return i; } bool typecompat(struct type lhs, struct type rhs) { switch (lhs.kind) { case TYPE_FN: return lhs.ret == rhs.ret; case TYPE_NUM: if (rhs.size == 0) return true; return lhs.issigned == rhs.issigned && lhs.size >= rhs.size; case TYPE_F16: case TYPE_F32: case TYPE_F64: return lhs.size >= rhs.size; case TYPE_RUNE: return lhs.kind == rhs.kind; } __builtin_unreachable(); }