commit 2b53ab23f3270c51d93b4b3ff300c65843a2a0a1
parent a53c0817a301913ec8242a2f472fad7968c9ee95
Author: Eyal Sawady <ecs@d2evs.net>
Date: Sun, 14 Feb 2021 14:17:01 -0500
Implement slice assignment
Diffstat:
3 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/src/check.c b/src/check.c
@@ -374,6 +374,11 @@ check_expr_assign(struct context *ctx,
check_expression(ctx, aexpr->assign.value, value, object->result);
assert(object->type == EXPR_ACCESS
|| object->type == EXPR_SLICE); // Invariant
+ if (object->type == EXPR_SLICE) {
+ expect(&aexpr->assign.object->loc,
+ expr->assign.op == BIN_LEQUAL,
+ "Slice assignments may not have a binop");
+ }
expect(&aexpr->loc, !(object->result->flags & TYPE_CONST),
"Cannot assign to const object");
expect(&aexpr->loc,
diff --git a/src/gen.c b/src/gen.c
@@ -750,7 +750,58 @@ gen_expr_assign_slice(struct gen_context *ctx,
const struct expression *expr,
const struct qbe_value *out)
{
- assert(0); // TODO
+ struct qbe_value obj = {0}, val = {0}, temp = {0};
+ assert(expr->assign.op == BIN_LEQUAL && !expr->assign.indirect);
+ constl(&temp, builtin_type_size.size);
+ alloc_temp(ctx, &obj, expr->assign.object->result, "assign.object.%d");
+ alloc_temp(ctx, &val,expr->assign.value->result, "assign.value.%d");
+ gen_expression(ctx, expr->assign.object, &obj);
+ gen_expression(ctx, expr->assign.value, &val);
+
+ struct qbe_value ptr = {0}, olen = {0}, vlen = {0};
+ gen_temp(ctx, &ptr, &qbe_long, "assign.lenptr.%d");
+ gen_temp(ctx, &olen, &qbe_long, "assign.olen.%d");
+ gen_temp(ctx, &vlen, &qbe_long, "assign.vlen.%d");
+
+ qval_deref(&obj);
+ qval_deref(&val);
+ pushi(ctx->current, &ptr, Q_COPY, &obj, NULL);
+ pushi(ctx->current, &ptr, Q_ADD, &ptr, &temp, NULL);
+ pushi(ctx->current, &olen, Q_LOADL, &ptr, NULL);
+ pushi(ctx->current, &ptr, Q_COPY, &val, NULL);
+ pushi(ctx->current, &ptr, Q_ADD, &ptr, &temp, NULL);
+ pushi(ctx->current, &vlen, Q_LOADL, &ptr, NULL);
+
+ struct qbe_statement equall = {0}, diffl = {0};
+ struct qbe_value bequal = {0}, bdiff = {0};
+ bequal.kind = QV_LABEL;
+ bequal.name = strdup(genl(&equall, &ctx->id, "equal.%d"));
+ bdiff.kind = QV_LABEL;
+ bdiff.name = strdup(genl(&diffl, &ctx->id, "diff.%d"));
+ gen_temp(ctx, &temp, &qbe_long, "assign.equal.%d");
+ pushi(ctx->current, &temp, Q_SUB, &olen, &vlen, NULL);
+ pushi(ctx->current, NULL, Q_JNZ, &temp, &bdiff, &bequal, NULL);
+ push(&ctx->current->body, &diffl);
+
+ struct qbe_value rtabort = {0};
+ rtabort.kind = QV_GLOBAL;
+ rtabort.name = strdup("rt.abort_fixed");
+ rtabort.type = &qbe_long;
+ constl(&temp, 0);
+ pushi(ctx->current, NULL, Q_CALL, &rtabort, &temp, NULL);
+ push(&ctx->current->body, &equall);
+
+ struct qbe_value rtmemcpy = {0}, optr = {0}, vptr = {0};
+ rtmemcpy.kind = QV_GLOBAL;
+ rtmemcpy.name = strdup("rt.memcpy");
+ rtmemcpy.type = &qbe_long;
+ gen_temp(ctx, &optr, &qbe_long, "assign.optr.%d");
+ pushi(ctx->current, &optr, Q_LOADL, &obj, NULL);
+ gen_temp(ctx, &vptr, &qbe_long, "assign.vptr.%d");
+ pushi(ctx->current, &vptr, Q_LOADL, &val, NULL);
+ constl(&temp, expr->assign.object->result->array.members->size);
+ pushi(ctx->current, &olen, Q_MUL, &olen, &temp, NULL);
+ pushi(ctx->current, NULL, Q_CALL, &rtmemcpy, &optr, &vptr, &olen, NULL);
}
static void
diff --git a/tests/08-slices.ha b/tests/08-slices.ha
@@ -55,6 +55,10 @@ fn indexing() void = {
) != 0, "indexing non-array, non-slice object");
};
+fn zero3(s: []int) void = {
+ s[..] = [0, 0, 0];
+};
+
fn assignment() void = {
let source = [1, 2, 3];
let x: []int = source;
@@ -70,6 +74,17 @@ fn assignment() void = {
x[2] = 9;
assert(x[0] == 7 && x[1] == 8 && x[2] == 9);
assert(source[0] == 4 && source[1] == 5 && source[2] == 6);
+
+ zero3(y);
+ assert(y[0] == 0 && y[1] == 0 && y[2] == 0);
+ let z: []int = [1, 2, 3, 4, 5];
+ z[1..4] = [42, 69, 1337];
+ assert(z[0] == 1 && z[1] == 42 && z[2] == 69 && z[3] == 1337 && z[4] == 5);
+ z[2..5] = y;
+ assert(z[0] == 1 && z[1] == 42 && z[2] == 0 && z[3] == 0 && z[4] == 0);
+ assert(rt::compile(
+ "export fn main() void = { let a: []int = [1]; a[..] += a; };"
+ ) != 0, "binop slice assignment");
};
fn assert_slice_eq(actual: []int, expected: []int) void = {