commit f7e8c9f1b41ff5c9430147fe483b1b890552a140
parent 8bc045be450b1a4f5fd0b8da11315a26fee8fd4b
Author: Drew DeVault <sir@cmpwn.com>
Date: Tue, 2 Feb 2021 17:23:27 -0500
gen: implement match transitivity
Diffstat:
M | src/gen.c | | | 38 | +++++++++++++++++++++++++++++--------- |
M | tests/18-match.ha | | | 60 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 89 insertions(+), 9 deletions(-)
diff --git a/src/gen.c b/src/gen.c
@@ -1515,17 +1515,37 @@ gen_match_tagged(struct gen_context *ctx,
fbranch.kind = QV_LABEL;
fbranch.name = strdup(genl(&flabel, &ctx->id, "next.case.%d"));
- const struct type *intermediate =
- tagged_select_subtype(mtype, _case->type);
- if (intermediate->id != _case->type->id) {
- assert(0); // TODO
- }
+ struct qbe_value temp_tag = {0}, subval = {0}, offs = {0};
+ gen_temp(ctx, &subval, &qbe_long, "subtag.ptr.%d");
+ gen_temp(ctx, &temp_tag, &qbe_word, "subtag.tag.%d");
+ pushi(ctx->current, &subval, Q_COPY, &mval, NULL);
+ struct qbe_value *curtag = &tag;
+ const struct type *subtype = mtype;
+ const struct type *test = _case->type;
+ do {
+ struct qbe_statement slabel = {0};
+ struct qbe_value sbranch = {0};
+ sbranch.kind = QV_LABEL;
+ sbranch.name = strdup(genl(&slabel, &ctx->id, "match.subtype.%d"));
+
+ test = tagged_select_subtype(subtype, _case->type);
+ constw(&match, test->id);
+ pushi(ctx->current, &temp, Q_CEQW, &match, curtag, NULL);
+ pushi(ctx->current, NULL, Q_JNZ, &temp, &sbranch, &fbranch, NULL);
+ push(&ctx->current->body, &slabel);
+
+ if (test->id != _case->type->id) {
+ constl(&offs, subtype->align);
+ pushi(ctx->current, &subval, Q_ADD, &subval, &offs, NULL);
+ pushi(ctx->current, &temp_tag, Q_LOADUW, &subval, NULL);
+ curtag = &temp_tag;
+ }
- constw(&match, _case->type->id);
- pushi(ctx->current, &temp, Q_CEQW, &match, &tag, NULL);
- pushi(ctx->current, NULL, Q_JNZ, &temp, &tbranch, &fbranch, NULL);
- push(&ctx->current->body, &tlabel);
+ subtype = test;
+ } while (test->id != _case->type->id);
+ pushi(ctx->current, NULL, Q_JMP, &tbranch, NULL);
+ push(&ctx->current->body, &tlabel);
if (_case->object) {
struct qbe_value val = {0}, temp = {0};
gen_temp(ctx, &val,
diff --git a/tests/18-match.ha b/tests/18-match.ha
@@ -92,6 +92,65 @@ fn implicit_cast() void = {
assert(a is foobar);
};
+type foo = void;
+type bar = void;
+type foobar = (foo | bar);
+type baz = int;
+type foobarbaz = (foobar | baz);
+
+fn transitivity() void = {
+ let x: (foobar | int) = 10;
+ match (x) {
+ i: int => assert(i == 10),
+ foo => abort(),
+ bar => abort(),
+ };
+
+ x = foo;
+ let visit = false;
+ match (x) {
+ int => abort(),
+ foo => {
+ visit = true;
+ },
+ bar => abort(),
+ };
+ assert(visit);
+
+ x = bar;
+ visit = false;
+ match (x) {
+ int => abort(),
+ foo => abort(),
+ foobar => {
+ visit = true;
+ },
+ };
+ assert(visit);
+
+ let y: foobarbaz = 10;
+ visit = false;
+ match (y) {
+ baz => {
+ visit = true;
+ },
+ foo => abort(),
+ bar => abort(),
+ };
+ assert(visit);
+
+ y = foo;
+ visit = false;
+ match (y) {
+ baz => abort(),
+ foo => {
+ visit = true;
+ },
+ bar => abort(),
+ };
+ assert(visit);
+};
+
export fn main() void = {
tagged();
termination();
@@ -100,5 +159,6 @@ export fn main() void = {
alias();
tagged_result();
implicit_cast();
+ transitivity();
// TODO: Test exhaustiveness and dupe detection
};