harec

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

commit 7f5ecdada36bf67187d1b7e2b624df9d1d2264eb
parent c61a7ceab56b78061ddf99c4802572f75ba42382
Author: Drew DeVault <sir@cmpwn.com>
Date:   Mon,  9 Aug 2021 09:55:08 +0200

gen: implement match subset types

The copy/cast thing on the incompatible case is a bit of a hack.

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

Diffstat:
Minclude/expr.h | 1+
Msrc/gen.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++----
Mtests/913-match.ha | 17+++++++++++++++++
3 files changed, 68 insertions(+), 4 deletions(-)

diff --git a/include/expr.h b/include/expr.h @@ -358,6 +358,7 @@ struct expression { struct expression_slice slice; struct expression_tuple tuple; struct expression_unarithm unarithm; + void *user; }; }; diff --git a/src/gen.c b/src/gen.c @@ -9,6 +9,8 @@ #include "types.h" #include "util.h" +#define EXPR_GEN_VALUE -1 + static const struct gen_value gv_void = { .kind = GV_CONST, .type = &builtin_type_void, @@ -555,7 +557,16 @@ gen_expr_cast_tagged_at(struct gen_context *ctx, { const struct type *to = expr->result, *from = expr->cast.value->result; const struct type *subtype = tagged_select_subtype(to, from); - assert(subtype || tagged_subset_compat(to, from)); + if ((int)expr->cast.value->type != EXPR_GEN_VALUE) { + // EXPR_GEN_VALUE is a special case used by match for + // COMPAT_MISALIGNED. Normally, casts are only allowed between + // tagged unions if 'to' is a superset of 'from'. However, in + // the EXPR_GEN_VALUE case, 'to' is a subset of 'from', but the + // match code has already determined that the selected tag is a + // member of 'to'. + assert(subtype || tagged_subset_compat(to, from)); + } + if (!subtype && tagged_align_compat(from, to)) { // Case 1: from is a union whose members are a subset of to, and // the alignment matches, so we can just interpret values of @@ -1146,7 +1157,18 @@ gen_subset_match_tests(struct gen_context *ctx, // // In this situation, we test the match object's tag against each type // ID of the case type. - assert(0); // TODO + const struct type *casetype = type_dealias(_case->type); + for (const struct type_tagged_union *tu = &casetype->tagged; + tu; tu = tu->next) { + struct qbe_statement lnexttag; + struct qbe_value bnexttag = mklabel(ctx, &lnexttag, ".%d"); + struct qbe_value id = constl(tu->type->id); + struct qbe_value match = mkqtmp(ctx, &qbe_word, ".%d"); + pushi(ctx->current, &match, Q_CEQW, &tag, &id, NULL); + pushi(ctx->current, NULL, Q_JNZ, &match, &bmatch, &bnexttag, NULL); + push(&ctx->current->body, &lnexttag); + } + pushi(ctx->current, NULL, Q_JMP, &bnext, NULL); } static struct gen_value @@ -1222,8 +1244,29 @@ gen_match_with_tagged(struct gen_context *ctx, pushi(ctx->current, &qv, Q_ADD, &qobject, &offset, NULL); break; case COMPAT_ALIGNED: + pushi(ctx->current, &qv, Q_COPY, &qobject, NULL); + break; case COMPAT_MISALIGNED: - assert(0); // TODO + ; + enum qbe_instr alloc = alloc_for_align(_case->type->align); + struct qbe_value sz = constl(_case->type->size); + pushprei(ctx->current, &qv, alloc, &sz, NULL); + struct expression value = { + .type = EXPR_GEN_VALUE, + .result = objtype, + .user = &object, + }; + struct expression cast = { + .type = EXPR_CAST, + .result = _case->type, + .cast = { + .kind = C_CAST, + .secondary = _case->type, + .value = &value, + }, + }; + gen_expr_at(ctx, &cast, gb->value); + break; } next: @@ -1570,7 +1613,7 @@ gen_expr_unarithm(struct gen_context *ctx, static struct gen_value gen_expr(struct gen_context *ctx, const struct expression *expr) { - switch (expr->type) { + switch ((int)expr->type) { case EXPR_ACCESS: return gen_expr_access(ctx, expr); case EXPR_ALLOC: @@ -1624,6 +1667,9 @@ gen_expr(struct gen_context *ctx, const struct expression *expr) case EXPR_STRUCT: case EXPR_TUPLE: break; // Prefers -at style + // gen-specific psuedo-expressions + case EXPR_GEN_VALUE: + return *(struct gen_value *)expr->user; } struct gen_value out = mktemp(ctx, expr->result, "object.%d"); diff --git a/tests/913-match.ha b/tests/913-match.ha @@ -23,6 +23,22 @@ fn nested_subtype() void = { }; }; +fn subset() void = { + // Alignment compatible + let x: (int | size | void) = 1337z; + match (x) { + n: (int | size) => assert(n as size == 1337), + void => abort(), + }; + + // Alignment incompatible + let x: (int | size | void) = 1337; + match (x) { + n: (int | void) => assert(n as int == 1337), + size => abort(), + }; +}; + fn pointer() void = { let x = 42; let y: nullable *int = &x; @@ -43,6 +59,7 @@ fn pointer() void = { export fn main() int = { subtype(); nested_subtype(); + subset(); pointer(); return 0; };