harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit f7e8c9f1b41ff5c9430147fe483b1b890552a140
parent 8bc045be450b1a4f5fd0b8da11315a26fee8fd4b
Author: Drew DeVault <sir@cmpwn.com>
Date:   Tue,  2 Feb 2021 17:23:27 -0500

gen: implement match transitivity

Diffstat:
Msrc/gen.c | 38+++++++++++++++++++++++++++++---------
Mtests/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 };