harec

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

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:
Msrc/check.c | 43++++++++++++++++++++++++++++++++++++++-----
Mtests/19-append.ha | 33+++++++++++++++++++++++++++++++++
Mtests/28-insert.ha | 22+++++++++++++++++++++-
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(); };