commit 9c2ad2b58a6cdb6012fb559a90a45c681390e85a
parent 727c5de862700a15dabb3863167bbbf6ddecadd7
Author: Drew DeVault <sir@cmpwn.com>
Date: Sat, 13 Feb 2021 11:32:43 -0500
gen: more casts between alignment-distinct taggeds
Diffstat:
2 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/src/gen.c b/src/gen.c
@@ -1085,7 +1085,35 @@ gen_cast_to_tagged(struct gen_context *ctx,
struct qbe_value tag = {0}, ptr = {0}, offs = {0};
constl(&offs, expr->result->align);
- if (!subtype) {
+ if (!subtype && type_dealias(from)->storage == TYPE_STORAGE_TAGGED
+ && from->align != tagged->align
+ && type_dealias(tagged)->size != builtin_type_uint.size
+ && type_dealias(from)->size != builtin_type_uint.size) {
+ // If the alignment differs, we can't use a straight-up copy
+ struct qbe_value src = {0}, dest = {0};
+ pushc(ctx->current, "to_tagged; converting incompatible");
+ alloc_temp(ctx, &src, from, "to_tagged.from.%d");
+ qval_deref(&src);
+ gen_expression(ctx, expr->cast.value, &src);
+
+ gen_temp(ctx, &dest,
+ qtype_for_type(ctx, tagged, false), "to_tagged.to.%d");
+ pushi(ctx->current, &dest, Q_COPY, out, NULL);
+
+ gen_temp(ctx, &tag, &qbe_word, "to_tagged.tag.%d");
+ pushi(ctx->current, &tag, Q_LOADUW, &src, NULL);
+ pushi(ctx->current, NULL, Q_STOREW, &tag, &dest, NULL);
+
+ constl(&offs, tagged->align);
+ pushi(ctx->current, &dest, Q_ADD, &dest, &offs, NULL);
+ constl(&offs, from->align);
+ pushi(ctx->current, &src, Q_ADD, &src, &offs, NULL);
+
+ dest.type = dest.type->fields.next->type;
+ src.type = src.type->fields.next->type;
+ gen_copy(ctx, &dest, &src);
+ return;
+ } else if (!subtype) {
pushc(ctx->current, "to_tagged; no subtype");
alloc_temp(ctx, &ptr, tagged, "to_tagged.from.%d");
qval_deref(&ptr);
diff --git a/tests/18-match.ha b/tests/18-match.ha
@@ -190,6 +190,9 @@ export fn alignment_conversion() void = {
y: align_4 => assert(y as int == 1234),
* => abort(),
};
+ let y: align_4 = 4321i;
+ x = y: align_8;
+ assert(x as int == 4321);
};
export fn main() void = {