diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-03-22 13:53:23 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-03-22 13:53:23 -0400 |
| commit | 20d75db79a2b74697d93f395e9d07f43f46e9c6b (patch) | |
| tree | 887b3f629205644be2c5a3a0d73dc63b0ae963b7 | |
| parent | ddee8692109b17750e1573ca7773c02726ac8f2b (diff) | |
Fix/improve assignment to arrays and tables
| -rw-r--r-- | builtins/array.h | 14 | ||||
| -rw-r--r-- | compile.c | 47 |
2 files changed, 48 insertions, 13 deletions
diff --git a/builtins/array.h b/builtins/array.h index 209a72e2..6375f6d4 100644 --- a/builtins/array.h +++ b/builtins/array.h @@ -11,12 +11,20 @@ #include "types.h" // Convert negative indices to back-indexed without branching: index0 = index + (index < 0)*(len+1)) - 1 -#define $Array_get(type, x, i, filename, start, end) *({ \ - const array_t $arr = x; int64_t $index = (int64_t)(i); \ +#define $Array_get(item_type, arr_expr, index_expr, filename, start, end) *({ \ + const array_t $arr = arr_expr; int64_t $index = (int64_t)(index_expr); \ int64_t $off = $index + ($index < 0) * ($arr.length + 1) - 1; \ if (__builtin_expect($off < 0 || $off >= $arr.length, 0)) \ fail_source(filename, start, end, "Invalid array index: %r (array has length %ld)\n", Int__as_text(&$index, USE_COLOR, NULL), $arr.length); \ - (type*)($arr.data + $arr.stride * $off);}) + (item_type*)($arr.data + $arr.stride * $off);}) +#define $Array_set(item_type, arr_expr, index_expr, value_expr, typeinfo, filename, start, end) { \ + array_t *$arr = arr_expr; int64_t $index = (int64_t)(index_expr); \ + int64_t $off = $index + ($index < 0) * ($arr->length + 1) - 1; \ + if (__builtin_expect($off < 0 || $off >= $arr->length, 0)) \ + fail_source(filename, start, end, "Invalid array index: %r (array has length %ld)\n", Int__as_text(&$index, USE_COLOR, NULL), $arr->length); \ + if ($arr->data_refcount > 0) \ + Array__compact($arr, typeinfo); \ + *(item_type*)($arr->data + $arr->stride * $off) = value_expr; } #define $Array_get_unchecked(type, x, i) *({ const array_t $arr = x; int64_t $index = (int64_t)(i); \ int64_t $off = $index + ($index < 0) * ($arr.length + 1) - 1; \ (type*)($arr.data + $arr.stride * $off);}) @@ -14,6 +14,8 @@ #include "typecheck.h" #include "builtins/util.h" +static CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool allow_optional); + CORD compile_type_ast(env_t *env, type_ast_t *t) { switch (t->tag) { @@ -116,13 +118,41 @@ static void check_assignable(env_t *env, ast_t *ast) if (!can_be_mutated(env, ast)) { if (ast->tag == Index || ast->tag == FieldAccess) { ast_t *subject = ast->tag == Index ? Match(ast, Index)->indexed : Match(ast, FieldAccess)->fielded; - code_err(subject, "This is a readonly pointer, which can't be assigned to"); + code_err(subject, "This is an immutable value, you can't assign to it"); } else { code_err(ast, "This is a value of type %T and can't be assigned to", get_type(env, ast)); } } } +static CORD compile_assignment(env_t *env, ast_t *target, CORD value) +{ + check_assignable(env, target); + if (target->tag == Index) { + auto index = Match(target, Index); + type_t *container_t = get_type(env, index->indexed); + container_t = value_type(container_t); + switch (container_t->tag) { + case ArrayType: { + CORD target_code = compile_to_pointer_depth(env, index->indexed, 1, false); + type_t *item_type = Match(container_t, ArrayType)->item_type; + return CORD_all("$Array_set(", compile_type(env, item_type), ", ", target_code, ", ", + compile(env, index->index), ", ", value, ", ", compile_type_info(env, container_t), + ", ", Text__quoted(target->file->filename, false), ", ", heap_strf("%ld", target->start - target->file->text), + ", ", heap_strf("%ld", target->end - target->file->text), ");\n"); + } + case TableType: { + CORD target_code = compile_to_pointer_depth(env, index->indexed, 1, false); + return CORD_all("Table_set_value(", target_code, ", ", compile(env, index->index), ", ", + value, ", ", compile_type_info(env, container_t), ");\n"); + } + case PointerType: break; + default: code_err(target, "I don't know how to assign to this target"); + } + } + return CORD_all(compile(env, target), " = ", value, ";\n"); +} + CORD compile_statement(env_t *env, ast_t *ast) { switch (ast->tag) { @@ -194,7 +224,7 @@ CORD compile_statement(env_t *env, ast_t *ast) // Common case: assigning to one variable: check_assignable(env, assign->targets->ast); CORD var = compile(env, assign->targets->ast); - CORD code = CORD_all(var, " = ", compile(env, assign->values->ast), ";"); + CORD code = compile_assignment(env, assign->targets->ast, compile(env, assign->values->ast)); CORD_appendf(&code, "$test(&%r, %r, %r, %r, %ld, %ld);", var, compile_type_info(env, get_type(env, assign->targets->ast)), compile(env, WrapAST(test->expr, TextLiteral, .cord=test->output)), @@ -212,10 +242,8 @@ CORD compile_statement(env_t *env, ast_t *ast) for (ast_list_t *value = assign->values; value; value = value->next) CORD_appendf(&code, "%r $%ld = %r;\n", compile_type(env, get_type(env, value->ast)), i++, compile(env, value->ast)); i = 1; - for (ast_list_t *target = assign->targets; target; target = target->next) { - check_assignable(env, target->ast); - CORD_appendf(&code, "%r = $%ld;\n", compile(env, target->ast), i++); - } + for (ast_list_t *target = assign->targets; target; target = target->next) + code = CORD_all(code, compile_assignment(env, target->ast, CORD_asprintf("$%ld", i++))); CORD_appendf(&code, "$test(&$1, %r, %r, %r, %ld, %ld);", compile_type_info(env, get_type(env, assign->targets->ast)), @@ -273,7 +301,7 @@ CORD compile_statement(env_t *env, ast_t *ast) auto assign = Match(ast, Assign); // Single assignment: if (assign->targets && !assign->targets->next) - return CORD_asprintf("%r = %r;", compile(env, assign->targets->ast), compile(env, assign->values->ast)); + return compile_assignment(env, assign->targets->ast, compile(env, assign->values->ast)); CORD code = "{ // Assignment\n"; int64_t i = 1; @@ -281,8 +309,7 @@ CORD compile_statement(env_t *env, ast_t *ast) CORD_appendf(&code, "%r $%ld = %r;\n", compile_type(env, get_type(env, value->ast)), i++, compile(env, value->ast)); i = 1; for (ast_list_t *target = assign->targets; target; target = target->next) { - check_assignable(env, target->ast); - CORD_appendf(&code, "%r = $%ld;\n", compile(env, target->ast), i++); + code = CORD_cat(code, compile_assignment(env, target->ast, CORD_asprintf("$%ld", i++))); } return CORD_cat(code, "\n}"); } @@ -745,7 +772,7 @@ CORD compile_string(env_t *env, ast_t *ast, CORD color) return expr_as_text(env, expr, t, color); } -static CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool allow_optional) +CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool allow_optional) { CORD val = compile(env, ast); type_t *t = get_type(env, ast); |
