commit 4bd3edc59b5f4defe8e3fddcd0ddc4bf8bc2bf24
parent d4bd9749de1ea29690b9f9d4d9c6a04c69da05bc
Author: Drew DeVault <sir@cmpwn.com>
Date: Fri, 6 Aug 2021 14:38:20 +0200
gen: implement cast-to-tagged case 2
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
4 files changed, 77 insertions(+), 3 deletions(-)
diff --git a/include/type_store.h b/include/type_store.h
@@ -47,6 +47,10 @@ const struct type *type_store_lookup_alias(struct type_store *store,
const struct type *type_store_lookup_tagged(struct type_store *store,
struct type_tagged_union *tags);
+// Returns a (non-tagged) union of the members of a tagged union type
+const struct type *type_store_tagged_to_union(
+ struct type_store *store, const struct type *tagged);
+
const struct type *type_store_lookup_tuple(struct type_store *store,
struct type_tuple *values);
diff --git a/src/gen.c b/src/gen.c
@@ -476,8 +476,34 @@ gen_expr_cast_at(struct gen_context *ctx,
gen_expr_at(ctx, expr->cast.value, out2);
return;
} else if (!subtype) {
- // Case 2: alignment mismatch, so (much) more work is required.
- assert(0); // TODO
+ // Case 2: like case 1, but with an alignment mismatch; more
+ // work is required.
+ struct gen_value value = gen_expr(ctx, expr->cast.value);
+ struct qbe_value qval = mkqval(ctx, &value);
+ struct qbe_value qout = mkqval(ctx, &out);
+ struct qbe_value tag = mkqtmp(ctx,
+ qtype_lookup(ctx, &builtin_type_uint, false), "tag.%d");
+ enum qbe_instr load = load_for_type(ctx, &builtin_type_uint);
+ enum qbe_instr store = store_for_type(ctx, &builtin_type_uint);
+ pushi(ctx->current, &tag, load, &qval, NULL);
+ pushi(ctx->current, NULL, store, &tag, &qout, NULL);
+ if (to->size == builtin_type_uint.size ||
+ from->size == builtin_type_uint.size) {
+ // No data area to copy
+ return;
+ }
+
+ const struct type *innertype = type_store_tagged_to_union(
+ ctx->store, type_dealias(to));
+ struct gen_value iout = mktemp(ctx, innertype, ".%d");
+ struct gen_value ival = mktemp(ctx, innertype, ".%d");
+ struct qbe_value qiout = mkqval(ctx, &iout);
+ struct qbe_value qival = mkqval(ctx, &ival);
+ struct qbe_value offs = constl(to->align);
+ pushi(ctx->current, &qiout, Q_ADD, &qout, &offs, NULL);
+ offs = constl(from->align);
+ pushi(ctx->current, &qival, Q_ADD, &qval, &offs, NULL);
+ gen_copy_aligned(ctx, iout, ival);
return;
}
diff --git a/src/type_store.c b/src/type_store.c
@@ -847,6 +847,40 @@ type_store_lookup_tagged(struct type_store *store,
}
const struct type *
+type_store_tagged_to_union(struct type_store *store, const struct type *tagged)
+{
+ assert(tagged->storage == STORAGE_TAGGED);
+ struct type type = {
+ .storage = STORAGE_UNION,
+ .flags = tagged->flags,
+ };
+ struct struct_field **next = &type.struct_union.fields;
+ for (const struct type_tagged_union *tu = &tagged->tagged;
+ tu; tu = tu->next) {
+ if (tu->type->size == 0) {
+ continue;
+ }
+ assert(tu->type->size != SIZE_UNDEFINED);
+
+ if (tu->type->size > type.size) {
+ type.size = tu->type->size;
+ }
+ if (tu->type->align > type.align) {
+ type.align = tu->type->align;
+ }
+
+ struct struct_field *sf =
+ xcalloc(1, sizeof(struct struct_field));
+ sf->name = "unnamed";
+ sf->type = tu->type;
+ sf->next = *next, *next = sf;
+ next = &sf->next;
+ }
+ type.struct_union.c_compat = true; // XXX: Unsure about this
+ return type_store_lookup_type(store, &type);
+}
+
+const struct type *
type_store_lookup_tuple(struct type_store *store, struct type_tuple *values)
{
struct type type = {
diff --git a/tests/910-tagged.ha b/tests/910-tagged.ha
@@ -50,7 +50,17 @@ fn subsetcast() void = {
assert(p.data.z == 1337z);
// Disjoint alignment
- // TODO
+ let x: (int | void) = 1337;
+ let y: (size | int | void) = x;
+ let p = &y: *struct {
+ tag: uint,
+ data: union {
+ z: size,
+ i: int,
+ },
+ };
+ assert(p.tag == 1737287038);
+ assert(p.data.i == 1337);
};
export fn main() int = {