commit c8cdca8b7b08f62bb7d660e4247082f7c4ff14d7
parent 9886c903b7c4e919d70e324ed730ef143c6d0b67
Author: Bor Grošelj Simić <bor.groseljsimic@telemach.net>
Date: Mon, 24 Jan 2022 05:28:40 +0100
append/insert: detect and report errors
Signed-off-by: Bor Grošelj Simić <bor.groseljsimic@telemach.net>
Diffstat:
3 files changed, 92 insertions(+), 6 deletions(-)
diff --git a/src/check.c b/src/check.c
@@ -425,15 +425,18 @@ check_expr_append_insert(struct context *ctx,
assert(expr->append.object->type == EXPR_ACCESS);
const struct type *sltype;
+ const char *exprtype_name;
switch (expr->type) {
case EXPR_APPEND:
sltype = type_dereference(expr->append.object->result);
sltype = type_dealias(sltype);
+ exprtype_name = "append";
break;
case EXPR_INSERT:
assert(expr->append.object->access.type == ACCESS_INDEX);
sltype = expr->append.object->access.array->result;
sltype = type_dealias(sltype);
+ exprtype_name = "insert";
break;
default:
abort(); // Invariant
@@ -457,21 +460,51 @@ check_expr_append_insert(struct context *ctx,
expr->append.value = xcalloc(sizeof(struct expression), 1);
check_expression(ctx, aexpr->append.value, expr->append.value, valuehint);
+ if (!expr->append.is_multi && !aexpr->append.length) {
+ check_expression(ctx, aexpr->append.value, expr->append.value,
+ sltype->array.members);
+ if (!type_is_assignable(sltype->array.members,
+ expr->append.value->result)) {
+ error(ctx, aexpr->append.value->loc, expr,
+ "Value type must be assignable to object member type");
+ return;
+ }
+ expr->append.value = lower_implicit_cast(
+ sltype->array.members, expr->append.value);
+ return;
+ }
+
+ const struct type *valtype = type_dereference(expr->append.value->result);
+ valtype = type_dealias(valtype);
if (aexpr->append.length) {
+ if (valtype->storage != STORAGE_ARRAY
+ || !valtype->array.expandable) {
+ error(ctx, aexpr->append.value->loc, expr,
+ "Value must be an expandable array in append with length");
+ return;
+ }
struct expression *len = xcalloc(sizeof(struct expression), 1);
check_expression(ctx, aexpr->append.length, len, &builtin_type_size);
if (!type_is_assignable(&builtin_type_size, len->result)) {
- error(ctx, aexpr->loc, expr,
+ error(ctx, aexpr->append.length->loc, expr,
"Length parameter must be assignable to size");
return;
}
len = lower_implicit_cast(&builtin_type_size, len);
expr->append.length = len;
+ } else {
+ if (valtype->storage != STORAGE_SLICE
+ && valtype->storage != STORAGE_ARRAY) {
+ error(ctx, aexpr->append.value->loc, expr,
+ "Value must be an array or a slice in multi-valued %s",
+ exprtype_name);
+ return;
+ }
}
-
- if (!expr->append.is_multi && !expr->append.length) {
- expr->append.value = lower_implicit_cast(
- sltype->array.members, expr->append.value);
+ if (sltype->array.members != valtype->array.members) {
+ error(ctx, aexpr->loc, expr,
+ "Value member type must match object member type");
+ return;
}
}
diff --git a/tests/19-append.ha b/tests/19-append.ha
@@ -1,3 +1,5 @@
+use rt;
+
fn basics() void = {
let x: []int = [];
append(x, 1);
@@ -53,9 +55,40 @@ fn withlength() void = {
free(x);
};
+fn reject() void = {
+ assert(rt::compile("
+ let x: []u8 = [0u8];
+ let y: int = 42;
+ append(x, y);
+ ") != 0); // object member type != value type
+ assert(rt::compile("
+ let x: []u8 = [0u8];
+ let y = 42u8;
+ append(x, y...);
+ ") != 0); // value is not an array or a slice
+ assert(rt::compile("
+ let x: []u8 = [0u8];
+ let y: []int = [42];
+ append(x, y...);
+ ") != 0); // object member type != value member type
+ assert(rt::compile("
+ let x: []u8 = [0u8];
+ append(x, [42i...], 5);
+ ") != 0); // same as above, but for an expression with length
+ assert(rt::compile("
+ let x: []u8 = [0u8];
+ append(x, [0u8...], 2i);
+ ") != 0); // length expression is not assignable to size
+ assert(rt::compile("
+ let x: []u8 = [0u8];
+ append(x, [42], 3);
+ ") != 0); // must be an expandable array
+};
+
export fn main() void = {
basics();
multi();
_static();
withlength();
+ reject();
};
diff --git a/tests/28-insert.ha b/tests/28-insert.ha
@@ -1,3 +1,5 @@
+use rt;
+
fn basics() void = {
let x: []int = alloc([1, 2, 5]);
insert(x[2], 4);
@@ -48,9 +50,27 @@ fn _static() void = {
};
};
+fn reject() void = {
+ assert(rt::compile("
+ let x: []u8 = [0u8];
+ let y: int = 42;
+ insert(x[1], y);
+ ") != 0); // object member type != value type
+ assert(rt::compile("
+ let x: []u8 = [0u8];
+ let y = 42u8;
+ insert(x[1], y...);
+ ") != 0); // value is not an array or a slice
+ assert(rt::compile("
+ let x: []u8 = [0u8];
+ let y: []int = [42];
+ insert(x[1], y...);
+ ") != 0); // object member type != value member type
+};
+
export fn main() void = {
basics();
multi();
_static();
- return;
+ reject();
};