commit b96aea9a37ba479045816177108865ca3320a318
parent b0a25c424ad652c2e5f8270b85a4e4fd14450d15
Author: Drew DeVault <sir@cmpwn.com>
Date: Sun, 24 Jan 2021 09:37:13 -0500
gen: implement match against nullable pointer
Diffstat:
M | src/gen.c | | | 91 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
M | tests/18-match.ha | | | 18 | ++++++++++++++++++ |
2 files changed, 105 insertions(+), 4 deletions(-)
diff --git a/src/gen.c b/src/gen.c
@@ -1388,13 +1388,10 @@ gen_expr_list(struct gen_context *ctx,
}
static void
-gen_expr_match(struct gen_context *ctx,
+gen_match_tagged(struct gen_context *ctx,
const struct expression *expr,
const struct qbe_value *out)
{
- // TODO: Pointers
- assert(expr->match.value->result->storage == TYPE_STORAGE_TAGGED_UNION);
-
const struct type *mtype = expr->match.value->result;
struct qbe_value mval = {0}, tag = {0}, match = {0}, temp = {0};
// Kill me
@@ -1480,6 +1477,92 @@ gen_expr_match(struct gen_context *ctx,
}
static void
+gen_match_nullable(struct gen_context *ctx,
+ const struct expression *expr,
+ const struct qbe_value *out)
+{
+ struct qbe_value mval = {0}, temp = {0};
+ gen_temp(ctx, &mval, &qbe_long, "match.%d"); // XXX: ARCH
+ gen_temp(ctx, &temp, &qbe_long, "temp.%d"); // XXX: ARCH
+ gen_expression(ctx, expr->match.value, &mval);
+
+ struct qbe_statement olabel = {0};
+ struct qbe_value obranch = {0};
+ obranch.kind = QV_LABEL;
+ obranch.name = strdup(genl(&olabel, &ctx->id, "out.%d"));
+
+ struct match_case *_default = NULL;
+ for (struct match_case *_case = expr->match.cases;
+ _case; _case = _case->next) {
+ if (!_case->type) {
+ _default = _case;
+ continue;
+ }
+
+ struct qbe_statement tlabel = {0}, flabel = {0};
+ struct qbe_value tbranch = {0}, fbranch = {0};
+ tbranch.kind = QV_LABEL;
+ tbranch.name = strdup(genl(&tlabel, &ctx->id, "match.%d"));
+ fbranch.kind = QV_LABEL;
+ fbranch.name = strdup(genl(&flabel, &ctx->id, "next.case.%d"));
+
+ struct qbe_value zero = {0};
+ constl(&zero, 0);
+ pushi(ctx->current, &temp, Q_CEQL, &mval, &zero, NULL);
+
+ if (_case->type->storage == TYPE_STORAGE_NULL) {
+ pushi(ctx->current, NULL, Q_JNZ,
+ &temp, &tbranch, &fbranch, NULL);
+ } else {
+ pushi(ctx->current, NULL, Q_JNZ,
+ &temp, &fbranch, &tbranch, NULL);
+ }
+
+ push(&ctx->current->body, &tlabel);
+
+ if (_case->object) {
+ struct qbe_value val = {0};
+ alloc_temp(ctx, &val, _case->type, "bound.%d");
+ struct gen_binding *binding =
+ xcalloc(1, sizeof(struct gen_binding));
+ binding->name = strdup(val.name);
+ binding->object = _case->object;
+ binding->next = ctx->bindings;
+ ctx->bindings = binding;
+ gen_store(ctx, &val, &mval);
+ }
+
+ gen_expression(ctx, _case->value, out);
+ pushi(ctx->current, NULL, Q_JMP, &obranch, NULL);
+ push(&ctx->current->body, &flabel);
+ }
+
+ if (_default) {
+ gen_expression(ctx, _default->value, out);
+ }
+
+ push(&ctx->current->body, &olabel);
+}
+
+static void
+gen_expr_match(struct gen_context *ctx,
+ const struct expression *expr,
+ const struct qbe_value *out)
+{
+ const struct type *mtype = expr->match.value->result;
+ switch (mtype->storage) {
+ case TYPE_STORAGE_TAGGED_UNION:
+ gen_match_tagged(ctx, expr, out);
+ break;
+ case TYPE_STORAGE_POINTER:
+ gen_match_nullable(ctx, expr, out);
+ break;
+ default:
+ assert(0); // Invariant
+ }
+}
+
+static void
gen_expr_measure(struct gen_context *ctx,
const struct expression *expr,
const struct qbe_value *out)
diff --git a/tests/18-match.ha b/tests/18-match.ha
@@ -30,8 +30,26 @@ fn default() void = {
assert(y == 24);
};
+fn pointer() void = {
+ let x = 42;
+ let y: nullable *int = &x;
+ let z: int = match (y) {
+ y: *int => *y,
+ null => abort(),
+ };
+ assert(z == 42);
+
+ y = null;
+ z = match(y) {
+ *int => abort(),
+ null => 1337,
+ };
+ assert(z == 1337);
+};
+
export fn main() void = {
tagged();
termination();
default();
+ pointer();
};