harec

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

commit 7cfa80cf328004ef31a8cc23b3e9a768cca142bb
parent e351df55fcfe02c2ab6b2997ba4934e6f4f17583
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun,  8 Aug 2021 19:19:46 +0200

gen: implement nullable pointer match

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Msrc/gen.c | 90++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/qtype.c | 2+-
Mtests/913-match.ha | 18++++++++++++++++++
3 files changed, 106 insertions(+), 4 deletions(-)

diff --git a/src/gen.c b/src/gen.c @@ -1150,13 +1150,11 @@ gen_subset_match_tests(struct gen_context *ctx, } static struct gen_value -gen_expr_match_with(struct gen_context *ctx, +gen_match_with_tagged(struct gen_context *ctx, const struct expression *expr, struct gen_value *out) { const struct type *objtype = expr->match.value->result; - // TODO: Match on pointer type: - assert(type_dealias(objtype)->storage == STORAGE_TAGGED); struct gen_value object = gen_expr(ctx, expr->match.value); struct qbe_value qobject = mkqval(ctx, &object); struct qbe_value tag = mkqtmp(ctx, ctx->arch.sz, "tag.%d"); @@ -1201,6 +1199,9 @@ gen_expr_match_with(struct gen_context *ctx, goto next; } + // TODO: We actually need to allocate a separate binding and + // copy this into it, probably. We could avoid that if we knew + // the binding were not assigned to, fwiw. struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding)); gb->value.kind = GV_TEMP; gb->value.type = _case->type; @@ -1237,6 +1238,89 @@ next: } static struct gen_value +gen_match_with_nullable(struct gen_context *ctx, + const struct expression *expr, + struct gen_value *out) +{ + struct qbe_statement lout; + struct qbe_value bout = mklabel(ctx, &lout, ".%d"); + struct gen_value object = gen_expr(ctx, expr->match.value); + struct qbe_value qobject = mkqval(ctx, &object); + + 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 lmatch, lnext; + struct qbe_value bmatch = mklabel(ctx, &lmatch, "matches.%d"); + struct qbe_value bnext = mklabel(ctx, &lnext, "next.%d"); + + if (_case->type->storage == STORAGE_NULL) { + pushi(ctx->current, NULL, Q_JNZ, + &qobject, &bnext, &bmatch, NULL); + } else { + pushi(ctx->current, NULL, Q_JNZ, + &qobject, &bmatch, &bnext, NULL); + } + + push(&ctx->current->body, &lmatch); + + if (!_case->object) { + goto next; + } + + struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding)); + gb->value = mktemp(ctx, _case->type, "binding.%d"); + gb->object = _case->object; + gb->next = ctx->bindings; + ctx->bindings = gb; + + // TODO: We could avoid this allocation if we knew the user + // didn't mutate the binding. + enum qbe_instr store = store_for_type(ctx, _case->type); + enum qbe_instr alloc = alloc_for_align(_case->type->align); + struct qbe_value qv = mkqval(ctx, &gb->value); + struct qbe_value sz = constl(_case->type->size); + pushprei(ctx->current, &qv, alloc, &sz, NULL); + pushi(ctx->current, NULL, store, &qobject, &qv, NULL); + +next: + // TODO: Handle !out case + gen_expr_with(ctx, _case->value, out); + if (!_case->value->terminates) { + pushi(ctx->current, NULL, Q_JMP, &bout, NULL); + } + push(&ctx->current->body, &lnext); + } + + if (_default) { + gen_expr_with(ctx, _default->value, out); + } + + push(&ctx->current->body, &lout); + return gv_void; +} + +static struct gen_value +gen_expr_match_with(struct gen_context *ctx, + const struct expression *expr, + struct gen_value *out) +{ + const struct type *objtype = expr->match.value->result; + switch (type_dealias(objtype)->storage) { + case STORAGE_POINTER: + return gen_match_with_nullable(ctx, expr, out); + case STORAGE_TAGGED: + return gen_match_with_tagged(ctx, expr, out); + default: abort(); // Invariant + } +} + +static struct gen_value gen_expr_measure(struct gen_context *ctx, const struct expression *expr) { size_t len; diff --git a/src/qtype.c b/src/qtype.c @@ -204,6 +204,7 @@ const struct qbe_type *qtype_lookup( return ctx->arch.sz; case STORAGE_UINTPTR: case STORAGE_POINTER: + case STORAGE_NULL: return ctx->arch.ptr; case STORAGE_F32: return &qbe_single; @@ -226,7 +227,6 @@ const struct qbe_type *qtype_lookup( case STORAGE_FUNCTION: return ctx->arch.ptr; case STORAGE_VOID: - case STORAGE_NULL: case STORAGE_FCONST: case STORAGE_ICONST: abort(); // Invariant diff --git a/tests/913-match.ha b/tests/913-match.ha @@ -23,8 +23,26 @@ fn nested_subtype() void = { }; }; +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() int = { subtype(); nested_subtype(); + pointer(); return 0; };