commit 698eed8deb0ab97e7edb83e7dfb28a81fb1b9872
parent d457aa96373ce0d7cc57275cab4647a32fd52ce7
Author: Drew DeVault <sir@cmpwn.com>
Date: Sat, 16 Jan 2021 13:02:37 -0500
gen: implement type assertions
Diffstat:
4 files changed, 52 insertions(+), 7 deletions(-)
diff --git a/include/gen.h b/include/gen.h
@@ -9,6 +9,7 @@
enum fixed_aborts {
ABORT_OOB = 0,
+ ABORT_TYPE_ASSERTION = 1,
};
struct unit;
diff --git a/rt/abort.ha b/rt/abort.ha
@@ -7,8 +7,9 @@ export @noreturn @symbol("rt.abort") fn _abort(msg: str) void = {
};
// See include/gen.h
-const reasons: [1]str = [
+const reasons: [2]str = [
"slice or array access out of bounds", // 0
+ "type assertion failed", // 1
];
export @noreturn fn abort_fixed(i: int) void = _abort(reasons[i]);
diff --git a/src/gen.c b/src/gen.c
@@ -761,16 +761,14 @@ gen_cast_from_tagged(struct gen_context *ctx,
assert(0); // TODO
}
- // XXX: This has not been fully tested pending rules implementation
struct qbe_value ptr = {0}, offs = {0}, temp = {0};
- gen_temp(ctx, &ptr,
- qtype_for_type(ctx, expr->result, false), "tagged.%d");
+ gen_temp(ctx, &ptr, &qbe_long, "tagged.%d");
gen_expression(ctx, expr->cast.value, &ptr);
constl(&offs, 8);
pushi(ctx->current, &ptr, Q_ADD, &ptr, &offs, NULL);
ptr.type = qtype_for_type(ctx, expr->result, false);
- ptr.indirect = type_is_aggregate(expr->result);
+ qval_deref(&ptr);
if (ptr.indirect) {
gen_loadtemp(ctx, &temp, &ptr,
qtype_for_type(ctx, expr->result, false),
@@ -800,6 +798,45 @@ gen_expr_type_test(struct gen_context *ctx,
}
static void
+gen_expr_type_assertion(struct gen_context *ctx,
+ const struct expression *expr,
+ const struct qbe_value *out)
+{
+ // XXX: ARCH
+ const struct type *want = type_dealias(expr->cast.secondary),
+ *tagged = type_dealias(expr->cast.value->result);
+ struct qbe_value tag = {0}, in = {0}, id = {0}, result = {0};
+ gen_temp(ctx, &tag, &qbe_long, "tag.%d");
+ gen_temp(ctx, &in, qtype_for_type(ctx, tagged, false), "cast.in.%d");
+ qval_address(&in);
+ gen_expression(ctx, expr->cast.value, &in);
+ pushi(ctx->current, &tag, Q_LOADL, &in, NULL);
+ constl(&id, want->id);
+ gen_temp(ctx, &result, &qbe_word, "valid.%d");
+ pushi(ctx->current, &result, Q_CEQL, &tag, &id, NULL);
+
+ struct qbe_statement validl = {0}, invalidl = {0};
+ struct qbe_value bvalid = {0}, binvalid = {0};
+ bvalid.kind = QV_LABEL;
+ bvalid.name = strdup(genl(&validl, &ctx->id, "type.valid.%d"));
+ binvalid.kind = QV_LABEL;
+ binvalid.name = strdup(genl(&invalidl, &ctx->id, "type.invalid.%d"));
+
+ pushi(ctx->current, NULL, Q_JNZ, &result, &bvalid, &binvalid, NULL);
+
+ push(&ctx->current->body, &invalidl);
+
+ struct qbe_value rtfunc = {0};
+ rtfunc.kind = QV_GLOBAL;
+ rtfunc.name = strdup("rt.abort_fixed");
+ rtfunc.type = &qbe_long;
+ constl(&result, ABORT_TYPE_ASSERTION);
+ pushi(ctx->current, NULL, Q_CALL, &rtfunc, &result, NULL);
+
+ push(&ctx->current->body, &validl);
+}
+
+static void
gen_expr_cast(struct gen_context *ctx,
const struct expression *expr,
const struct qbe_value *out)
@@ -808,7 +845,8 @@ gen_expr_cast(struct gen_context *ctx,
case C_CAST:
break; // Handled below
case C_ASSERTION:
- assert(0); // TODO
+ gen_expr_type_assertion(ctx, expr, out);
+ break; // Handled below
case C_TEST:
gen_expr_type_test(ctx, expr, out);
return;
diff --git a/tests/13-tagged.ha b/tests/13-tagged.ha
@@ -65,10 +65,15 @@ fn reduction() void = {
assert(i is int);
};
+fn casts() void = {
+ let a: (i8 | i16) = 42i16;
+ assert(a as i16 == 42i16);
+};
+
export fn main() void = {
measurements();
storage();
operators();
reduction();
- // TODO: Expand this as other tagged union features become available
+ casts();
};