harec

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

commit fcf6b7e066ab2ba511d977ab21607565e46fa1b3
parent 8ced1ca30f987d1c22165e947bd83163c929d2ae
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun, 24 Jan 2021 16:29:59 -0500

Fix several problems with casting tagged unions

Diffstat:
Mrt/+linux/segmalloc.ha | 4+++-
Msrc/check.c | 3+--
Msrc/gen.c | 17++++++++++-------
Msrc/type_store.c | 112+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
4 files changed, 81 insertions(+), 55 deletions(-)

diff --git a/rt/+linux/segmalloc.ha b/rt/+linux/segmalloc.ha @@ -4,7 +4,9 @@ fn segmalloc(n: size) nullable *void = { PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0z); // TODO: remove the cast to nullable *void - return if (p: uintptr: int == -ENOMEM) null: nullable *void else p: nullable *void; + return + if (p: uintptr: int == -ENOMEM) null: nullable *void + else p: nullable *void; }; // Frees a segment allocated with segmalloc. diff --git a/src/check.c b/src/check.c @@ -412,8 +412,7 @@ check_expr_binding(struct context *ctx, expect(&aexpr->loc, type_is_assignable(ctx->store, type, initializer->result), "Initializer is not assignable to binding type"); - binding->initializer = - lower_implicit_cast(type, initializer); + binding->initializer = lower_implicit_cast(type, initializer); if (abinding->is_static) { struct expression *value = diff --git a/src/gen.c b/src/gen.c @@ -844,13 +844,16 @@ gen_cast_to_tagged(struct gen_context *ctx, const struct qbe_value *out, const struct type *from) { - if (from->storage == TYPE_STORAGE_TAGGED_UNION) { - assert(0); // TODO - } - struct qbe_value tag = {0}, ptr = {0}, offs = {0}; gen_temp(ctx, &ptr, &qbe_long, "ptr.%d"); constl(&offs, expr->result->align); + + if (type_dealias(from)->storage == TYPE_STORAGE_TAGGED_UNION) { + gen_expression(ctx, expr->cast.value, &ptr); + gen_copy(ctx, out, &ptr); + return; + } + constw(&tag, expr->cast.value->result->id); pushi(ctx->current, &ptr, Q_COPY, out, NULL); pushi(ctx->current, NULL, Q_STOREW, &tag, &ptr, NULL); @@ -866,7 +869,7 @@ gen_cast_from_tagged(struct gen_context *ctx, const struct qbe_value *out, const struct type *to) { - if (to->storage == TYPE_STORAGE_TAGGED_UNION) { + if (type_dealias(to)->storage == TYPE_STORAGE_TAGGED_UNION) { assert(0); // TODO } @@ -893,7 +896,7 @@ gen_expr_type_test(struct gen_context *ctx, const struct qbe_value *out) { // XXX: ARCH - const struct type *want = type_dealias(expr->cast.secondary), + const struct type *want = expr->cast.secondary, *tagged = type_dealias(expr->cast.value->result); struct qbe_value tag = {0}, in = {0}, id = {0}; gen_temp(ctx, &tag, &qbe_word, "tag.%d"); @@ -962,7 +965,7 @@ gen_expr_cast(struct gen_context *ctx, const struct type *to = type_dealias(expr->result), *from = type_dealias(expr->cast.value->result); - if (to->storage == from->storage) { + if (to->storage == from->storage && to->size == from->size) { gen_expression(ctx, expr->cast.value, out); return; } else if (to->storage == TYPE_STORAGE_TAGGED_UNION) { diff --git a/src/type_store.c b/src/type_store.c @@ -186,17 +186,36 @@ type_is_assignable(struct type_store *store, } static bool -tagged_castable(const struct type *to, const struct type *from) +castable_to_tagged(const struct type *to, const struct type *from) { - if (to->storage == TYPE_STORAGE_TAGGED_UNION) { - if (from->storage == TYPE_STORAGE_TAGGED_UNION) { + if (type_dealias(from)->storage == TYPE_STORAGE_TAGGED_UNION) { + return true; + } + + size_t ncastable = 0; + to = type_dealias(to); + for (const struct type_tagged_union *tu = &to->tagged; + tu; tu = tu->next) { + if (tu->type->id == from->id) { return true; } - assert(0); // TODO + if (type_is_castable(tu->type, from)) { + ++ncastable; + } + } + + return ncastable == 1; +} + +static bool +castable_from_tagged(const struct type *to, const struct type *from) +{ + if (type_dealias(to)->storage == TYPE_STORAGE_TAGGED_UNION) { + return true; } - // TODO: Update spec to make this consistent size_t ncastable = 0; + from = type_dealias(from); for (const struct type_tagged_union *tu = &from->tagged; tu; tu = tu->next) { if (tu->type->id == to->id) { @@ -213,69 +232,72 @@ tagged_castable(const struct type *to, const struct type *from) bool type_is_castable(const struct type *to, const struct type *from) { - to = type_dealias(to); - from = type_dealias(from); + if (type_dealias(to)->storage == TYPE_STORAGE_TAGGED_UNION) { + return castable_to_tagged(to, from); + } else if (type_dealias(from)->storage == TYPE_STORAGE_TAGGED_UNION) { + return castable_from_tagged(to, from); + } + + to = type_dealias(to), from = type_dealias(from); if (to == from) { return true; } - switch (from->storage) { - case TYPE_STORAGE_CHAR: - return to->storage == TYPE_STORAGE_U8; - case TYPE_STORAGE_ENUM: - return to->storage == TYPE_STORAGE_ENUM || type_is_integer(to); - case TYPE_STORAGE_F32: - case TYPE_STORAGE_F64: - return type_is_numeric(to); - case TYPE_STORAGE_U8: - if (to->storage == TYPE_STORAGE_CHAR) { - return true; - } - // Fallthrough - case TYPE_STORAGE_U32: - if (to->storage == TYPE_STORAGE_RUNE - && from->storage == TYPE_STORAGE_U32) { - return true; - } - // Fallthrough + switch (to->storage) { + case TYPE_STORAGE_I8: case TYPE_STORAGE_I16: case TYPE_STORAGE_I32: case TYPE_STORAGE_I64: - case TYPE_STORAGE_I8: case TYPE_STORAGE_INT: case TYPE_STORAGE_SIZE: case TYPE_STORAGE_U16: case TYPE_STORAGE_U64: case TYPE_STORAGE_UINT: - return to->storage == TYPE_STORAGE_ENUM || type_is_numeric(to); + return from->storage == TYPE_STORAGE_ENUM || type_is_numeric(from); + case TYPE_STORAGE_U8: + return from->storage == TYPE_STORAGE_ENUM + || type_is_numeric(from) + || from->storage == TYPE_STORAGE_CHAR; + case TYPE_STORAGE_U32: + return from->storage == TYPE_STORAGE_ENUM + || type_is_numeric(from) + || from->storage == TYPE_STORAGE_RUNE; + case TYPE_STORAGE_CHAR: + return from->storage == TYPE_STORAGE_U8; + case TYPE_STORAGE_RUNE: + return from->storage == TYPE_STORAGE_RUNE; + case TYPE_STORAGE_ENUM: + return from->storage == TYPE_STORAGE_ENUM || type_is_integer(from); + case TYPE_STORAGE_F32: + case TYPE_STORAGE_F64: + return type_is_numeric(from); case TYPE_STORAGE_UINTPTR: - return to->storage == TYPE_STORAGE_POINTER - || to->storage == TYPE_STORAGE_NULL - || type_is_numeric(to); + return from->storage == TYPE_STORAGE_POINTER + || from->storage == TYPE_STORAGE_NULL + || type_is_numeric(from); case TYPE_STORAGE_POINTER: - case TYPE_STORAGE_NULL: - return to->storage == TYPE_STORAGE_POINTER - || to->storage == TYPE_STORAGE_NULL - || to->storage == TYPE_STORAGE_UINTPTR; + if (from->storage == TYPE_STORAGE_STRING + && to->pointer.referent->storage == TYPE_STORAGE_CHAR + && to->pointer.referent->flags & TYPE_CONST) { + return true; + } + return from->storage == TYPE_STORAGE_POINTER + || from->storage == TYPE_STORAGE_NULL + || from->storage == TYPE_STORAGE_UINTPTR; case TYPE_STORAGE_SLICE: case TYPE_STORAGE_ARRAY: - return to->storage == TYPE_STORAGE_SLICE - || to->storage == TYPE_STORAGE_ARRAY; - case TYPE_STORAGE_TAGGED_UNION: - return tagged_castable(to, from); - case TYPE_STORAGE_STRING: - return to->storage == TYPE_STORAGE_POINTER - && to->pointer.referent->storage == TYPE_STORAGE_CHAR - && to->pointer.referent->flags & TYPE_CONST; - case TYPE_STORAGE_RUNE: - return to->storage == TYPE_STORAGE_U32; + return from->storage == TYPE_STORAGE_SLICE + || from->storage == TYPE_STORAGE_ARRAY; // Cannot be cast: case TYPE_STORAGE_BOOL: case TYPE_STORAGE_VOID: case TYPE_STORAGE_FUNCTION: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_UNION: + case TYPE_STORAGE_STRING: + case TYPE_STORAGE_NULL: return false; + case TYPE_STORAGE_TAGGED_UNION: case TYPE_STORAGE_ALIAS: assert(0); // Handled above }