commit d5330de0042e9ef162947f4f3594b01cfdd035a2
parent aa264f4772e86d3ff49e89a3887fe24e8c399b8d
Author: Sebastian <sebastian@sebsite.pw>
Date: Tue, 10 Jan 2023 23:39:04 -0500
check: fix abort when casting to tagged union containing alias
The following code now compiles as intended:
type a = b;
type b = int;
type c = (b | void);
export fn main() void = {
const v: a = 0;
v: c; // used to trigger abort() in gen.c
};
Signed-off-by: Sebastian <sebastian@sebsite.pw>
Diffstat:
4 files changed, 65 insertions(+), 27 deletions(-)
diff --git a/include/types.h b/include/types.h
@@ -190,7 +190,7 @@ uint32_t type_hash(const struct type *type);
const struct type *promote_const(const struct type *a, const struct type *b);
bool type_is_assignable(const struct type *to, const struct type *from);
-bool type_is_castable(const struct type *to, const struct type *from);
+const struct type *type_is_castable(const struct type *to, const struct type *from);
bool type_is_complete(const struct type *type);
const struct type *type_create_const(enum type_storage storage,
diff --git a/src/check.c b/src/check.c
@@ -1475,8 +1475,10 @@ check_expr_cast(struct context *ctx,
return;
}
break;
- case C_CAST:
- if (!type_is_castable(secondary, value->result)) {
+ case C_CAST:;
+ const struct type *intermediary =
+ type_is_castable(secondary, value->result);
+ if (intermediary == NULL) {
char *primarytypename = gen_typename(value->result);
char *secondarytypename = gen_typename(secondary);
error(ctx, aexpr->cast.type->loc, expr,
@@ -1486,6 +1488,18 @@ check_expr_cast(struct context *ctx,
free(secondarytypename);
return;
}
+ // intermediary type is required when casting to tagged union
+ // whose member is an alias of primary type, since gen.c asserts
+ // that the primary type is a direct member of the tagged union.
+ // The value is first cast to an intermediary type which is a
+ // direct member of the tagged union, before being cast to the
+ // tagged union itself.
+ expr->cast.value = xcalloc(1, sizeof(struct expression));
+ expr->cast.value->type = EXPR_CAST;
+ expr->cast.value->result = intermediary;
+ expr->cast.value->cast.kind = C_CAST;
+ expr->cast.value->cast.value = value;
+ expr->cast.value->cast.secondary = intermediary;
if (value->result->storage == STORAGE_RCONST) {
uint32_t max = 0;
switch (secondary->storage) {
diff --git a/src/types.c b/src/types.c
@@ -881,36 +881,38 @@ type_is_assignable(const struct type *to, const struct type *from)
assert(0); // Unreachable
}
-static bool
+static const struct type *
is_castable_with_tagged(const struct type *to, const struct type *from)
{
if (type_dealias(from)->storage == STORAGE_TAGGED
&& type_dealias(to)->storage == STORAGE_TAGGED) {
if (tagged_subset_compat(to, from) || tagged_subset_compat(from, to)) {
- return true;
+ return to;
}
}
if (type_dealias(to)->storage == STORAGE_TAGGED) {
- if (tagged_select_subtype(to, from) != NULL) {
- return true;
+ const struct type *subtype = tagged_select_subtype(to, from);
+ if (subtype != NULL) {
+ return subtype;
}
}
if (type_dealias(from)->storage == STORAGE_TAGGED) {
- if (tagged_select_subtype(from, to) != NULL) {
- return true;
+ const struct type *subtype = tagged_select_subtype(from, to);
+ if (subtype != NULL) {
+ return subtype;
}
}
- return false;
+ return NULL;
}
-bool
+const struct type *
type_is_castable(const struct type *to, const struct type *from)
{
if (to->storage == STORAGE_VOID) {
if (type_is_constant(from)) {
lower_const(from, NULL);
};
- return true;
+ return to;
}
if (type_dealias(from)->storage == STORAGE_TAGGED
@@ -921,13 +923,13 @@ type_is_castable(const struct type *to, const struct type *from)
const struct type *to_orig = to, *from_orig = from;
to = type_dealias(to), from = type_dealias(from);
if (to == from) {
- return true;
+ return to_orig;
}
struct type _to, _from;
to = strip_flags(to, &_to), from = strip_flags(from, &_from);
if (to->id == from->id) {
- return true;
+ return to_orig;
}
switch (from->storage) {
@@ -944,43 +946,54 @@ type_is_castable(const struct type *to, const struct type *from)
case STORAGE_U16:
case STORAGE_U64:
case STORAGE_UINT:
- return to->storage == STORAGE_ENUM || type_is_numeric(to);
+ return to->storage == STORAGE_ENUM || type_is_numeric(to)
+ ? to_orig : NULL;
case STORAGE_U8:
return to->storage == STORAGE_ENUM
|| type_is_numeric(to)
- || to->storage == STORAGE_CHAR;
+ || to->storage == STORAGE_CHAR
+ ? to_orig : NULL;
case STORAGE_U32:
return to->storage == STORAGE_ENUM
|| type_is_numeric(to)
- || to->storage == STORAGE_RUNE;
+ || to->storage == STORAGE_RUNE
+ ? to_orig : NULL;
case STORAGE_CHAR:
- return to->storage == STORAGE_U8;
+ return to->storage == STORAGE_U8
+ ? to_orig : NULL;
case STORAGE_RUNE:
- return to->storage == STORAGE_U32;
+ return to->storage == STORAGE_U32
+ ? to_orig : NULL;
case STORAGE_ENUM:
- return to->storage == STORAGE_ENUM || type_is_integer(from);
+ return to->storage == STORAGE_ENUM || type_is_integer(from)
+ ? to_orig : NULL;
case STORAGE_F32:
case STORAGE_F64:
- return type_is_numeric(to);
+ return type_is_numeric(to)
+ ? to_orig : NULL;
case STORAGE_UINTPTR:
return to->storage == STORAGE_POINTER
|| to->storage == STORAGE_NULL
|| type_is_numeric(to)
- || to->storage == STORAGE_ENUM;
+ || to->storage == STORAGE_ENUM
+ ? to_orig : NULL;
case STORAGE_POINTER:
return to->storage == STORAGE_POINTER
|| to->storage == STORAGE_NULL
- || to->storage == STORAGE_UINTPTR;
+ || to->storage == STORAGE_UINTPTR
+ ? to_orig : NULL;
case STORAGE_NULL:
return to->storage == STORAGE_POINTER
- || to->storage == STORAGE_UINTPTR;
+ || to->storage == STORAGE_UINTPTR
+ ? to_orig : NULL;
case STORAGE_SLICE:
case STORAGE_ARRAY:
return to->storage == STORAGE_SLICE
|| to->storage == STORAGE_ARRAY
|| (to->storage == STORAGE_POINTER
&& to->pointer.referent->storage == STORAGE_ARRAY
- && from->storage == STORAGE_SLICE);
+ && from->storage == STORAGE_SLICE)
+ ? to_orig : NULL;
// Cannot be cast:
case STORAGE_STRING:
case STORAGE_BOOL:
@@ -990,9 +1003,9 @@ type_is_castable(const struct type *to, const struct type *from)
case STORAGE_STRUCT:
case STORAGE_UNION:
case STORAGE_VALIST:
- return false;
+ return NULL;
case STORAGE_ERROR:
- return true;
+ return to_orig;
case STORAGE_TAGGED:
case STORAGE_ALIAS:
assert(0); // Handled above
diff --git a/tests/13-tagged.ha b/tests/13-tagged.ha
@@ -169,6 +169,11 @@ fn subsetcast() void = {
type foo = (int | void);
type bar = (size | foo);
+type t1 = t2;
+type t2 = int;
+type t3 = (t2 | void);
+type t4 = t2;
+type t5 = (t4 | void);
fn castout() void = {
let x: (int | void) = 1337;
@@ -181,6 +186,12 @@ fn castout() void = {
assert(a as int == 42);
assert(a: int == 42);
assert(a is int);
+
+ const a: t1 = 42;
+ const x = a: t3;
+ assert(x as t2 == 42);
+ const x = a: t5;
+ assert(x as t4 == 42);
};
fn assertions() void = {