harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 698eed8deb0ab97e7edb83e7dfb28a81fb1b9872
parent d457aa96373ce0d7cc57275cab4647a32fd52ce7
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat, 16 Jan 2021 13:02:37 -0500

gen: implement type assertions

Diffstat:
Minclude/gen.h | 1+
Mrt/abort.ha | 3++-
Msrc/gen.c | 48+++++++++++++++++++++++++++++++++++++++++++-----
Mtests/13-tagged.ha | 7++++++-
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(); };