commit 26f865e29ba17223d71143db62395cceaf3a9aa9
parent faa2bb7d3f1ae5ca68b43eddbf92d595bd4cd518
Author: Drew DeVault <sir@cmpwn.com>
Date: Wed, 30 Dec 2020 14:19:25 -0500
gen: implement assertion expressions
Note that, without a runtime, these don't do very much yet.
Diffstat:
5 files changed, 63 insertions(+), 16 deletions(-)
diff --git a/rt/Makefile b/rt/Makefile
@@ -1,5 +1,5 @@
libhart_srcs=\
- rt/assert.ha \
+ rt/abort.ha \
rt/malloc.ha \
rt/memcmp.ha \
rt/memcpy.ha \
diff --git a/rt/abort.ha b/rt/abort.ha
@@ -0,0 +1,7 @@
+export @noreturn @symbol("rt.abort") fn _abort(msg: str) void = {
+ const prefix = "Abort: ";
+ write(2, prefix: *char, len(prefix));
+ write(2, msg: *char, len(msg));
+ write(2, "\n": *char, 1);
+ kill(getpid(), SIGABRT);
+};
diff --git a/rt/assert.ha b/rt/assert.ha
@@ -1,12 +0,0 @@
-export @noreturn fn abort() void = kill(getpid(), SIGABRT);
-
-export @symbol("sys.assert") fn assert_(cond: bool, msg: str) void = {
- if (!cond) {
- const prefix = "Assertion failed: ";
- write(2, prefix: *char, len(prefix));
- write(2, msg: *char, len(msg));
- write(2, "\n": *char, 1);
- abort();
- };
-};
-
diff --git a/src/check.c b/src/check.c
@@ -685,7 +685,6 @@ check_function(struct context *ctx,
const struct ast_function_decl *afndecl = &adecl->function;
trenter(TR_CHECK, "function");
- assert(!afndecl->symbol); // TODO
const struct ast_type fn_atype = {
.storage = TYPE_STORAGE_FUNCTION,
@@ -701,7 +700,12 @@ check_function(struct context *ctx,
decl->type = DECL_FUNC;
decl->func.type = fntype;
decl->func.flags = afndecl->flags;
- mkident(ctx, &decl->ident, &afndecl->ident);
+
+ if (afndecl->symbol) {
+ decl->ident.name = strdup(afndecl->symbol);
+ } else {
+ mkident(ctx, &decl->ident, &afndecl->ident);
+ }
decl->func.scope = scope_push(&ctx->scope, TR_CHECK);
struct ast_function_parameters *params = afndecl->prototype.params;
diff --git a/src/gen.c b/src/gen.c
@@ -333,6 +333,53 @@ gen_expr_access(struct gen_context *ctx,
}
static void
+gen_expr_assert(struct gen_context *ctx,
+ const struct expression *expr,
+ const struct qbe_value *out)
+{
+ assert(!out); // Invariant
+ assert(expr->assert.message); // Invariant
+
+ struct qbe_statement failedl = {0}, passedl = {0};
+ struct qbe_value bfailed = {0}, bpassed = {0};
+
+ struct qbe_value msg = {0};
+
+ struct qbe_value rtfunc = {0};
+ rtfunc.kind = QV_GLOBAL;
+ rtfunc.name = strdup("rt.abort");
+ rtfunc.type = &qbe_long;
+
+ if (expr->assert.cond) {
+ bfailed.kind = QV_LABEL;
+ bfailed.name = strdup(genl(&failedl, &ctx->id, "failed.%d"));
+ bpassed.kind = QV_LABEL;
+ bpassed.name = strdup(genl(&passedl, &ctx->id, "passed.%d"));
+
+ struct qbe_value cond = {0};
+ gen_temp(ctx, &cond, &qbe_word, "assert.%d");
+ gen_expression(ctx, expr->assert.cond, &cond);
+
+ alloc_temp(ctx, &msg, &builtin_type_const_str, "msg.%d");
+ qval_deref(&msg);
+ gen_expression(ctx, expr->assert.message, &msg);
+
+ pushi(ctx->current, NULL, Q_JNZ, &cond, &bpassed, &bfailed, NULL);
+ push(&ctx->current->body, &failedl);
+ } else {
+ alloc_temp(ctx, &msg, &builtin_type_const_str, "msg.%d");
+ qval_deref(&msg);
+ gen_expression(ctx, expr->assert.message, &msg);
+ }
+
+ pushi(ctx->current, NULL, Q_CALL, &rtfunc, &msg, NULL);
+
+ if (expr->assert.cond) {
+ push(&ctx->current->body, &passedl);
+ }
+}
+
+static void
gen_expr_assign_ptr(struct gen_context *ctx,
const struct expression *expr,
const struct qbe_value *out)
@@ -795,7 +842,8 @@ gen_expression(struct gen_context *ctx,
gen_expr_access(ctx, expr, out);
break;
case EXPR_ASSERT:
- assert(0); // TODO
+ gen_expr_assert(ctx, expr, out);
+ break;
case EXPR_ASSIGN:
gen_expr_assign(ctx, expr, out);
break;