From a2e9476a0eb01dd64bc4dc7734748fc8a70d7360 Mon Sep 17 00:00:00 2001
From: Thomas Voss <mail@thomasvoss.com>
Date: Wed, 19 Jun 2024 21:49:29 +0200
Subject: Assert that functions actually return

---
 src/analyzer.c | 86 ++++++++++++++++++++++++++++++++++------------------------
 src/analyzer.h |  2 +-
 src/common.h   |  3 +-
 src/main.c     |  2 +-
 4 files changed, 55 insertions(+), 38 deletions(-)

(limited to 'src')

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,13 +260,37 @@ 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)
 {
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
-- 
cgit v1.2.3