harec

[hare] Hare compiler, written in C11 for POSIX OSs
Log | Files | Refs | README | LICENSE

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:
Minclude/types.h | 2+-
Msrc/check.c | 18++++++++++++++++--
Msrc/types.c | 61+++++++++++++++++++++++++++++++++++++------------------------
Mtests/13-tagged.ha | 11+++++++++++
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 = {