commit 39599274468de63e4e8ee484e616fb8c695d5f11
parent 422f2db26233f1649c30b4579650852e0c955179
Author: Drew DeVault <sir@cmpwn.com>
Date: Sun, 31 Jan 2021 09:45:25 -0500
types: implement tagged_subset_compat
Diffstat:
2 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/src/types.c b/src/types.c
@@ -375,6 +375,31 @@ tagged_select_subtype(const struct type *tagged, const struct type *subtype)
return NULL;
}
+static bool
+tagged_subset_compat(const struct type *to, const struct type *from)
+{
+ // Note: this implementation depends on the invariant that tagged union
+ // member types are sorted by their type ID.
+ to = type_dealias(to), from = type_dealias(from);
+ if (to->storage != TYPE_STORAGE_TAGGED || from->storage != TYPE_STORAGE_TAGGED) {
+ return false;
+ }
+ const struct type_tagged_union *to_tu = &to->tagged,
+ *from_tu = &from->tagged;
+ while (from_tu && to_tu) {
+ while (to_tu) {
+ if (to_tu->type->id == from_tu->type->id) {
+ from_tu = from_tu->next;
+ to_tu = to_tu->next;
+ break;
+ }
+ to_tu = to_tu->next;
+ }
+ }
+
+ return !from_tu;
+}
+
bool
type_is_assignable(const struct type *to, const struct type *from)
{
@@ -464,8 +489,8 @@ type_is_assignable(const struct type *to, const struct type *from)
&& to->array.length == SIZE_UNDEFINED
&& from->array.length != SIZE_UNDEFINED;
case TYPE_STORAGE_TAGGED:
- // XXX: Needs work!
- return tagged_select_subtype(to, from) != NULL || true;
+ return tagged_select_subtype(to, from) != NULL
+ || tagged_subset_compat(to, from);
// The following types are only assignable from themselves, and are
// handled above:
case TYPE_STORAGE_BOOL:
diff --git a/tests/13-tagged.ha b/tests/13-tagged.ha
@@ -55,13 +55,13 @@ fn reduction() void = {
const a: (i8 | i16) = 42i8;
const b: (i16 | i8) = a;
const c: (i8 | i16 | i32) = a;
- //assert(rt::compile(
- // // Cannot assign from more general type
- // "fn test() void = {
- // let a: (i8 | i16 | i32) = 42i8;
- // let b: (i8 | i16) = a;
- // };"
- //) != 0);
+ assert(rt::compile(
+ // Cannot assign from more general type
+ "fn test() void = {
+ let a: (i8 | i16 | i32) = 42i8;
+ let b: (i8 | i16) = a;
+ };"
+ ) != 0);
assert(a is i8 && b is i8 && c is i8);
assert(size((i8 | i16 | i32)) == size((i8 | (i16 | i32))));
assert(size(integer) == size(signed));