harec

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

commit a78994ee7377ec20b4de56432bde3113d205d684
parent edd4452053a3803ad9d4c304c1284f5ebba11487
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri, 15 Jan 2021 19:05:47 -0500

Implement `is` operator

Diffstat:
Minclude/expr.h | 1+
Msrc/check.c | 37+++++++++++++++++++++++++++++++++----
Msrc/gen.c | 28++++++++++++++++++++++++++++
Msrc/type_store.c | 24+++++++++++++++++++++++-
Mtests/13-tagged.ha | 12++++++++++++
5 files changed, 97 insertions(+), 5 deletions(-)

diff --git a/include/expr.h b/include/expr.h @@ -104,6 +104,7 @@ enum cast_kind { struct expression_cast { enum cast_kind kind; + const struct type *secondary; struct expression *value; }; diff --git a/src/check.c b/src/check.c @@ -442,12 +442,41 @@ check_expr_cast(struct context *ctx, trace(TR_CHECK, "cast"); expr->type = EXPR_CAST; expr->cast.kind = aexpr->cast.kind; - expr->cast.value = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->cast.value, expr->cast.value); - expr->result = type_store_lookup_atype(&ctx->store, aexpr->cast.type); + struct expression *value = expr->cast.value = + xcalloc(1, sizeof(struct expression)); + check_expression(ctx, aexpr->cast.value, value); + const struct type *secondary = expr->cast.secondary = + type_store_lookup_atype(&ctx->store, aexpr->cast.type); expect(&aexpr->cast.type->loc, - type_is_castable(expr->result, expr->cast.value->result), + type_is_castable(secondary, value->result), "Invalid cast"); + + if (aexpr->cast.kind == C_ASSERTION || aexpr->cast.kind == C_TEST) { + expect(&aexpr->cast.value->loc, + type_dealias(expr->cast.value->result)->storage + == TYPE_STORAGE_TAGGED_UNION, + "Expected a tagged union type"); + bool found = false; + for (const struct type_tagged_union *t = &value->result->tagged; + t; t = t->next) { + if (t->type->id == secondary->id) { + found = true; + break; + } + } + expect(&aexpr->cast.type->loc, found, + "Type is not a valid member of the tagged union type"); + } + + switch (aexpr->cast.kind) { + case C_CAST: + case C_ASSERTION: + expr->result = secondary; + break; + case C_TEST: + expr->result = &builtin_type_bool; + break; + } } static void diff --git a/src/gen.c b/src/gen.c @@ -739,10 +739,38 @@ gen_cast_from_tagged(struct gen_context *ctx, } static void +gen_expr_type_test(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}; + 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); + pushi(ctx->current, out, Q_CEQL, &tag, &id, NULL); +} + +static void gen_expr_cast(struct gen_context *ctx, const struct expression *expr, const struct qbe_value *out) { + switch (expr->cast.kind) { + case C_CAST: + break; // Handled below + case C_ASSERTION: + assert(0); // TODO + case C_TEST: + gen_expr_type_test(ctx, expr, out); + return; + } + const struct type *to = type_dealias(expr->result), *from = type_dealias(expr->cast.value->result); if (to->storage == from->storage) { diff --git a/src/type_store.c b/src/type_store.c @@ -167,6 +167,28 @@ type_is_assignable(struct type_store *store, assert(0); // Unreachable } +static bool +tagged_castable(const struct type *to, const struct type *from) +{ + if (to->storage == TYPE_STORAGE_TAGGED_UNION) { + assert(0); // TODO + } + + // TODO: Update spec to make this consistent + size_t ncastable = 0; + for (const struct type_tagged_union *tu = &from->tagged; + tu; tu = tu->next) { + if (tu->type->id == to->id) { + return true; + } + if (type_is_castable(tu->type, to)) { + ++ncastable; + } + } + + return ncastable == 1; +} + bool type_is_castable(const struct type *to, const struct type *from) { @@ -219,7 +241,7 @@ type_is_castable(const struct type *to, const struct type *from) return to->storage == TYPE_STORAGE_SLICE || to->storage == TYPE_STORAGE_ARRAY; case TYPE_STORAGE_TAGGED_UNION: - assert(0); // TODO + return tagged_castable(to, from); case TYPE_STORAGE_STRING: return to->pointer.referent->storage == TYPE_STORAGE_CHAR && to->pointer.referent->flags & TYPE_CONST; diff --git a/tests/13-tagged.ha b/tests/13-tagged.ha @@ -29,8 +29,20 @@ fn storage() void = { assert(y._u64 == 0xCAFEBABEDEADBEEFu64); }; +fn operators() void = { + let x: (u8 | u16 | u32 | u64) = 42u8; + assert(x is u8); + x = 1337u16; + assert(x is u16); + x = 0xCAFEBABEu32; + assert(x is u32); + x = 0xCAFEBABEDEADBEEFu64; + assert(x is u64); +}; + export fn main() void = { measurements(); storage(); + operators(); // TODO: Expand this as other tagged union features become available };