commit 84fc04c060cf92d0eace6533ed8ef4ebec9cd571
parent 6a23ad4504330166d04ca6b97e2e9df3b04b5ed7
Author: Sebastian <sebastian@sebsite.pw>
Date: Thu, 5 Jan 2023 18:00:39 -0500
check: add compile-time bounds checks for slicing
Compilation now fails when the end index is greater than the length of
the array (if the object being sliced is an array), or if the start
index is greater than the end index.
Implements: https://todo.sr.ht/~sircmpwn/hare/749
Signed-off-by: Sebastian <sebastian@sebsite.pw>
Diffstat:
2 files changed, 72 insertions(+), 0 deletions(-)
diff --git a/src/check.c b/src/check.c
@@ -2432,6 +2432,70 @@ check_expr_return(struct context *ctx,
}
static void
+slice_bounds_check(struct context *ctx, struct expression *expr)
+{
+ const struct type *atype = type_dereference(expr->slice.object->result);
+ const struct type *dtype = type_dealias(atype);
+
+ struct expression *start = NULL, *end = NULL;
+
+ if (expr->slice.end != NULL) {
+ end = xcalloc(1, sizeof(struct expression));
+ enum eval_result r = eval_expr(ctx, expr->slice.end, end);
+ if (r != EVAL_OK) {
+ free(end);
+ return;
+ }
+
+ if (dtype->storage == STORAGE_ARRAY) {
+ assert(dtype->array.length != SIZE_UNDEFINED);
+ if (end->constant.uval > dtype->array.length) {
+ error(ctx, expr->loc, expr,
+ "End index must not be greater than array length");
+ free(end);
+ return;
+ }
+ }
+ } else if (dtype->storage != STORAGE_ARRAY
+ || dtype->array.length == SIZE_UNDEFINED) {
+ return;
+ }
+
+ if (expr->slice.start == NULL) {
+ if (end) free(end);
+ return;
+ }
+ start = xcalloc(1, sizeof(struct expression));
+ enum eval_result r = eval_expr(ctx, expr->slice.start, start);
+ if (r != EVAL_OK) {
+ free(start);
+ if (end) free(end);
+ return;
+ }
+
+ if (dtype->storage == STORAGE_ARRAY
+ && dtype->array.length != SIZE_UNDEFINED) {
+ if (start->constant.uval > dtype->array.length) {
+ error(ctx, expr->loc, expr,
+ "Start index must not be greater than array length");
+ free(start);
+ if (end) free(end);
+ return;
+ }
+ }
+
+ if (end != NULL) {
+ if (start->constant.uval > end->constant.uval) {
+ error(ctx, expr->loc, expr,
+ "Start index must not be greater than end index");
+ }
+ free(end);
+ }
+ free(start);
+ return;
+}
+
+static void
check_expr_slice(struct context *ctx,
const struct ast_expression *aexpr,
struct expression *expr,
@@ -2490,6 +2554,8 @@ check_expr_slice(struct context *ctx,
return;
}
+ slice_bounds_check(ctx, expr);
+
if (dtype->storage == STORAGE_SLICE) {
expr->result = atype;
} else {
diff --git a/tests/08-slices.ha b/tests/08-slices.ha
@@ -136,6 +136,12 @@ fn slicing() void = {
assert(compile(
"fn test() void = { let x = [1, 2, 3]; x[\"hi\"..]; };"
) as exited != EXIT_SUCCESS);
+ assert(compile(
+ "fn test() void = { let x = [1, 2, 3]; x[2..1]; };"
+ ) as exited != EXIT_SUCCESS);
+ assert(compile(
+ "fn test() void = { let x = [1, 2, 3]; x[..4]; };"
+ ) as exited != EXIT_SUCCESS);
};
type tree = struct {