aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas Voss <mail@thomasvoss.com> 2024-06-19 21:49:29 +0200
committerThomas Voss <mail@thomasvoss.com> 2024-06-19 21:49:29 +0200
commita2e9476a0eb01dd64bc4dc7734748fc8a70d7360 (patch)
tree16244f9e1d7ebe3e79926fb7460a5beaaf47fdcd /src
parent3ea67fa6af035b3f5b2865e6eeb39ceea49bf07c (diff)
Assert that functions actually return
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.c86
-rw-r--r--src/analyzer.h2
-rw-r--r--src/common.h3
-rw-r--r--src/main.c2
4 files changed, 55 insertions, 38 deletions
diff --git a/src/analyzer.c b/src/analyzer.c
index dc22190..d563b84 100644
--- a/src/analyzer.c
+++ b/src/analyzer.c
@@ -24,40 +24,32 @@ struct evstack {
size_t len, cap;
};
-struct typechkctx {
+struct azctx {
struct type fnret;
struct strview decl;
+ bool chkrets;
};
-static void typechkast(struct evstack, struct type *, struct ast,
+static void analyzeast(struct evstack, struct type *, struct ast,
struct lexemes)
__attribute__((nonnull));
-static idx_t_ typechkdecl(struct typechkctx, struct evstack, struct type *,
- struct ast, struct lexemes, idx_t_)
- __attribute__((nonnull));
-static idx_t_ typechkstmt(struct typechkctx, struct evstack, struct type *,
- struct ast, struct lexemes, idx_t_)
- __attribute__((nonnull));
-static idx_t_ typechkexpr(struct typechkctx, struct evstack, struct type *,
- struct ast, struct lexemes, idx_t_)
- __attribute__((nonnull));
-static idx_t_ typechkfn(struct typechkctx, struct evstack, struct type *,
- struct ast, struct lexemes, idx_t_)
- __attribute__((nonnull));
-static idx_t_ typechkblk(struct typechkctx, struct evstack, struct type *,
- struct ast, struct lexemes, idx_t_)
+
+typedef idx_t_ analyzer(struct azctx, struct evstack, struct type *, struct ast,
+ struct lexemes, idx_t_)
__attribute__((nonnull));
+static analyzer analyzeblk, analyzedecl, analyzeexpr, analyzefn, analyzestmt;
static const struct type *typegrab(struct ast, struct lexemes, idx_t_)
__attribute__((returns_nonnull));
static bool typecompat(struct type, struct type);
+static bool returns(struct ast, idx_t_);
/* Defined in primitives.gperf */
const struct type *typelookup(const uchar *, size_t)
__attribute__((nonnull));
struct type *
-analyzeast(struct ast ast, struct lexemes toks)
+analyzeprog(struct ast ast, struct lexemes toks)
{
arena a = NULL;
struct evstack evs = {.cap = 16};
@@ -65,7 +57,7 @@ analyzeast(struct ast ast, struct lexemes toks)
struct type *types = bufalloc(NULL, ast.len, sizeof(*types));
memset(types, 0, ast.len * sizeof(*types));
- typechkast(evs, types, ast, toks);
+ analyzeast(evs, types, ast, toks);
arena_free(&a);
free(evs.buf);
@@ -83,7 +75,7 @@ typegrab(struct ast ast, struct lexemes toks, idx_t_ i)
}
void
-typechkast(struct evstack evs, struct type *types, struct ast ast,
+analyzeast(struct evstack evs, struct type *types, struct ast ast,
struct lexemes toks)
{
struct environ ev = {.cap = 16};
@@ -102,17 +94,17 @@ typechkast(struct evstack evs, struct type *types, struct ast ast,
evs.buf[0] = ev;
evs.len = 1;
- struct typechkctx ctx = {0};
+ struct azctx ctx = {0};
for (idx_t_ i = 0; likely(i < ast.len); i = fwdnode(ast, i)) {
assert(ast.kinds[i] <= _AST_DECLS_END);
- typechkdecl(ctx, evs, types, ast, toks, i);
+ analyzedecl(ctx, evs, types, ast, toks, i);
}
free(ev.buf);
}
idx_t_
-typechkdecl(struct typechkctx ctx, struct evstack evs, struct type *types,
+analyzedecl(struct azctx ctx, struct evstack evs, struct type *types,
struct ast ast, struct lexemes toks, idx_t_ i)
{
ctx.decl = toks.strs[ast.lexemes[i]];
@@ -129,7 +121,7 @@ typechkdecl(struct typechkctx ctx, struct evstack evs, struct type *types,
if (p.lhs != AST_EMPTY)
ltype = *typegrab(ast, toks, p.lhs);
if (p.rhs != AST_EMPTY) {
- ni = typechkexpr(ctx, evs, types, ast, toks, p.rhs);
+ ni = analyzeexpr(ctx, evs, types, ast, toks, p.rhs);
rtype = types[p.rhs];
} else
ni = fwdnode(ast, i);
@@ -144,13 +136,13 @@ typechkdecl(struct typechkctx ctx, struct evstack evs, struct type *types,
}
idx_t_
-typechkstmt(struct typechkctx ctx, struct evstack evs, struct type *types,
+analyzestmt(struct azctx ctx, struct evstack evs, struct type *types,
struct ast ast, struct lexemes toks, idx_t_ i)
{
switch (ast.kinds[i]) {
case ASTDECL:
case ASTCDECL:
- return typechkdecl(ctx, evs, types, ast, toks, i);
+ return analyzedecl(ctx, evs, types, ast, toks, i);
case ASTRET: {
idx_t_ expr = ast.kids[i].rhs;
if (expr == AST_EMPTY) {
@@ -160,7 +152,7 @@ typechkstmt(struct typechkctx ctx, struct evstack evs, struct type *types,
} else if (ctx.fnret.kind == TYPE_UNSET)
err("analyzer: Function has no return value");
- idx_t_ ni = typechkexpr(ctx, evs, types, ast, toks, ast.kids[i].rhs);
+ idx_t_ ni = analyzeexpr(ctx, evs, types, ast, toks, ast.kids[i].rhs);
if (!typecompat(ctx.fnret, types[ast.kids[i].rhs]))
err("analyzer: Return type mismatch");
return ni;
@@ -172,7 +164,7 @@ typechkstmt(struct typechkctx ctx, struct evstack evs, struct type *types,
}
idx_t_
-typechkexpr(struct typechkctx ctx, struct evstack evs, struct type *types,
+analyzeexpr(struct azctx ctx, struct evstack evs, struct type *types,
struct ast ast, struct lexemes toks, idx_t_ i)
{
switch (ast.kinds[i]) {
@@ -203,7 +195,7 @@ typechkexpr(struct typechkctx ctx, struct evstack evs, struct type *types,
switch (types[j].kind) {
case TYPE_UNSET:
evs.len = lvl + 1;
- typechkdecl(ctx, evs, types, ast, toks, ev.buf[j].astidx);
+ analyzedecl(ctx, evs, types, ast, toks, ev.buf[j].astidx);
break;
case TYPE_CHECKING:
err("analyzer: Circular definition of ‘%.*s’", (int)sv.len,
@@ -218,7 +210,7 @@ typechkexpr(struct typechkctx ctx, struct evstack evs, struct type *types,
err("analyzer: Unknown constant ‘%.*s’", (int)sv.len, sv.p);
}
case ASTFN:
- return typechkfn(ctx, evs, types, ast, toks, i);
+ return analyzefn(ctx, evs, types, ast, toks, i);
default:
err("analyzer: Unexpected AST kind %u", ast.kinds[i]);
__builtin_unreachable();
@@ -226,7 +218,7 @@ typechkexpr(struct typechkctx ctx, struct evstack evs, struct type *types,
}
idx_t_
-typechkfn(struct typechkctx ctx, struct evstack evs, struct type *types,
+analyzefn(struct azctx ctx, struct evstack evs, struct type *types,
struct ast ast, struct lexemes toks, idx_t_ i)
{
struct type t = {.kind = TYPE_FN};
@@ -236,15 +228,15 @@ typechkfn(struct typechkctx ctx, struct evstack evs, struct type *types,
if (ast.kids[proto].rhs != AST_EMPTY) {
t.ret = typegrab(ast, toks, ast.kids[proto].rhs);
ctx.fnret = *t.ret;
+ ctx.chkrets = true;
} else
ctx.fnret.kind = TYPE_UNSET;
types[i] = t;
- idx_t_ ni = typechkblk(ctx, evs, types, ast, toks, p.rhs);
- return ni;
+ return analyzeblk(ctx, evs, types, ast, toks, p.rhs);
}
idx_t_
-typechkblk(struct typechkctx ctx, struct evstack evs, struct type *types,
+analyzeblk(struct azctx ctx, struct evstack evs, struct type *types,
struct ast ast, struct lexemes toks, idx_t_ i)
{
struct environ ev = {.cap = 16};
@@ -268,14 +260,38 @@ typechkblk(struct typechkctx ctx, struct evstack evs, struct type *types,
}
evs.buf[evs.len++] = ev;
- for (i = p.lhs; i <= p.rhs; i = typechkstmt(ctx, evs, types, ast, toks, i))
- ;
+ bool chkrets = ctx.chkrets, hasret = false;
+ ctx.chkrets = false;
+
+ for (i = p.lhs; i <= p.rhs;) {
+ if (chkrets && returns(ast, i))
+ hasret = true;
+ i = analyzestmt(ctx, evs, types, ast, toks, i);
+ }
+
+ if (chkrets && !hasret)
+ err("analyzer: Function doesn’t return on all paths");
free(ev.buf);
return i;
}
bool
+returns(struct ast ast, idx_t_ i)
+{
+ switch (ast.kinds[i]) {
+ case ASTDECL:
+ case ASTPDECL:
+ case ASTCDECL:
+ case ASTPCDECL:
+ return false;
+ case ASTRET:
+ return true;
+ }
+ __builtin_unreachable();
+}
+
+bool
typecompat(struct type lhs, struct type rhs)
{
/* Function types are compatible if they have the same parameter- and
diff --git a/src/analyzer.h b/src/analyzer.h
index 261427c..726b4e5 100644
--- a/src/analyzer.h
+++ b/src/analyzer.h
@@ -29,7 +29,7 @@ struct type {
idx_t_ paramcnt;
};
-struct type *analyzeast(struct ast, struct lexemes)
+struct type *analyzeprog(struct ast, struct lexemes)
__attribute__((returns_nonnull));
#endif /* !ORYX_ANALYZER_H */
diff --git a/src/common.h b/src/common.h
index 5756c83..d0cdbda 100644
--- a/src/common.h
+++ b/src/common.h
@@ -5,8 +5,9 @@
# define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)
#else
+# include <stdlib.h>
# define __attribute__(x)
-# define __builtin_unreachable() (((char *)0)[0] = 0)
+# define __builtin_unreachable() abort()
# define likely(x) (x)
# define unlikely(x) (x)
#endif
diff --git a/src/main.c b/src/main.c
index a61b860..6cb715a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -29,7 +29,7 @@ main(int argc, char **argv)
struct lexemes toks = lexstring(src, srclen);
struct ast ast = parsetoks(toks);
- struct type *types = analyzeast(ast, toks);
+ struct type *types = analyzeprog(ast, toks);
// codegen(argv[1], types, ast, toks);
#if DEBUG