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:
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
}