From 71d6787541e829781e17d90f6b57eabcb17fb8a9 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 27 Oct 2024 19:33:28 -0400 Subject: Bugfix: for array:find() and array:sorted() and array:binary_search(), do an ARRAY_COPY() if a user closure is being passed in, because the closure could mutate the array and the semantics of those functions should be to return information based on a snapshot --- compile.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'compile.c') diff --git a/compile.c b/compile.c index d8cbf8b4..3f8a8d3a 100644 --- a/compile.c +++ b/compile.c @@ -2669,7 +2669,9 @@ CORD compile(env_t *env, ast_t *ast) (void)compile_arguments(env, ast, NULL, call->args); return CORD_all("Array$shuffled(", self, ", ", padded_item_size, ")"); } else if (streq(call->name, "sort") || streq(call->name, "sorted")) { - CORD self = compile_to_pointer_depth(env, call->self, streq(call->name, "sort") ? 1 : 0, false); + CORD self = streq(call->name, "sort") + ? compile_to_pointer_depth(env, call->self, 1, false) + : compile_to_pointer_depth(env, call->self, 0, call->args != NULL); CORD comparison; if (call->args) { type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); @@ -2720,7 +2722,7 @@ CORD compile(env_t *env, ast_t *ast) CORD arg_code = compile_arguments(env, ast, arg_spec, call->args); return CORD_all("Array$heap_pop_value(", self, ", ", arg_code, ", ", padded_item_size, ", ", compile_type(item_t), ")"); } else if (streq(call->name, "binary_search")) { - CORD self = compile_to_pointer_depth(env, call->self, 0, false); + CORD self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), .ret=Type(IntType, .bits=TYPE_IBITS32)); @@ -2742,7 +2744,7 @@ CORD compile(env_t *env, ast_t *ast) return CORD_all("Array$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type_info(env, self_value_t), ")"); } else if (streq(call->name, "first")) { - CORD self = compile_to_pointer_depth(env, call->self, 0, false); + CORD self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); type_t *predicate_type = Type( ClosureType, .fn=Type(FunctionType, .args=new(arg_t, .name="item", .type=item_ptr), .ret=Type(BoolType))); -- cgit v1.2.3 From df258c1773adef59994da2e010f74610eb81a1b2 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 27 Oct 2024 19:39:34 -0400 Subject: Remove an unnecessary ARRAY_COPY() --- compile.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'compile.c') diff --git a/compile.c b/compile.c index 3f8a8d3a..c88e2a7f 100644 --- a/compile.c +++ b/compile.c @@ -2671,7 +2671,8 @@ CORD compile(env_t *env, ast_t *ast) } else if (streq(call->name, "sort") || streq(call->name, "sorted")) { CORD self = streq(call->name, "sort") ? compile_to_pointer_depth(env, call->self, 1, false) - : compile_to_pointer_depth(env, call->self, 0, call->args != NULL); + // No need to do an ARRAY_COPY() here because it happens inside Array$sorted(): + : compile_to_pointer_depth(env, call->self, 0, false); CORD comparison; if (call->args) { type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); -- cgit v1.2.3 From 41c0ea851a542bcd7d54b8c5c06d70e1e00095e1 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 27 Oct 2024 19:54:48 -0400 Subject: Deprecate "&" for stack references --- compile.c | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) (limited to 'compile.c') diff --git a/compile.c b/compile.c index c88e2a7f..a0069b34 100644 --- a/compile.c +++ b/compile.c @@ -506,8 +506,6 @@ CORD compile_statement(env_t *env, ast_t *ast) if (!assign->targets->next && assign->targets->ast->tag == Var && is_idempotent(assign->targets->ast)) { // Common case: assigning to one variable: type_t *lhs_t = get_type(env, assign->targets->ast); - if (lhs_t->tag == PointerType && Match(lhs_t, PointerType)->is_stack) - code_err(test->expr, "Stack references cannot be assigned to local variables because the variable may outlive the stack memory."); env_t *val_scope = with_enum_scope(env, lhs_t); type_t *rhs_t = get_type(val_scope, assign->values->ast); CORD value; @@ -535,8 +533,6 @@ CORD compile_statement(env_t *env, ast_t *ast) int64_t i = 1; for (ast_list_t *target = assign->targets, *value = assign->values; target && value; target = target->next, value = value->next) { type_t *target_type = get_type(env, target->ast); - if (target_type->tag == PointerType && Match(target_type, PointerType)->is_stack) - code_err(ast, "Stack references cannot be assigned to local variables because the variable may outlive the stack memory."); env_t *val_scope = with_enum_scope(env, target_type); type_t *value_type = get_type(val_scope, value->ast); CORD val_code; @@ -609,8 +605,6 @@ CORD compile_statement(env_t *env, ast_t *ast) // Single assignment, no temp vars needed: if (assign->targets && !assign->targets->next && is_idempotent(assign->targets->ast)) { type_t *lhs_t = get_type(env, assign->targets->ast); - if (lhs_t->tag == PointerType && Match(lhs_t, PointerType)->is_stack) - code_err(ast, "Stack references cannot be assigned to local variables because the variable may outlive the stack memory."); env_t *val_env = with_enum_scope(env, lhs_t); type_t *rhs_t = get_type(val_env, assign->values->ast); CORD val; @@ -628,8 +622,6 @@ CORD compile_statement(env_t *env, ast_t *ast) int64_t i = 1; for (ast_list_t *value = assign->values, *target = assign->targets; value && target; value = value->next, target = target->next) { type_t *lhs_t = get_type(env, target->ast); - if (lhs_t->tag == PointerType && Match(lhs_t, PointerType)->is_stack) - code_err(ast, "Stack references cannot be assigned to local variables because the variable may outlive the stack memory."); env_t *val_env = with_enum_scope(env, lhs_t); type_t *rhs_t = get_type(val_env, value->ast); CORD val; @@ -1518,7 +1510,7 @@ CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool val = CORD_all("(&", val, ")"); else code_err(ast, "This should be a pointer, not %T", get_type(env, ast)); - t = Type(PointerType, .pointed=t, .is_stack=true); + t = Type(PointerType, .pointed=t, .is_view=true); ++depth; } else { auto ptr = Match(t, PointerType); @@ -1978,13 +1970,6 @@ CORD compile(env_t *env, ast_t *ast) } // TODO: for constructors, do new(T, ...) instead of heap((T){...}) case HeapAllocate: return CORD_asprintf("heap(%r)", compile(env, Match(ast, HeapAllocate)->value)); - case StackReference: { - ast_t *subject = Match(ast, StackReference)->value; - if (can_be_mutated(env, subject)) - return CORD_all("(&", compile_lvalue(env, subject), ")"); - else - code_err(subject, "This subject can't be mutated!"); - } case Optional: { ast_t *value = Match(ast, Optional)->value; CORD value_code = compile(env, value); @@ -2352,7 +2337,7 @@ CORD compile(env_t *env, ast_t *ast) static int64_t comp_num = 1; const char *comprehension_name = heap_strf("arr$%ld", comp_num++); ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), - .type=Type(PointerType, .pointed=array_type, .is_stack=true)); + .type=Type(PointerType, .pointed=array_type, .is_view=true)); Closure_t comp_action = {.fn=add_to_array_comprehension, .userdata=comprehension_var}; scope->comprehension_action = &comp_action; CORD code = CORD_all("({ Array_t ", comprehension_name, " = {};"); @@ -2431,7 +2416,7 @@ CORD compile(env_t *env, ast_t *ast) env_t *scope = fresh_scope(env); const char *comprehension_name = heap_strf("table$%ld", comp_num++); ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), - .type=Type(PointerType, .pointed=table_type, .is_stack=true)); + .type=Type(PointerType, .pointed=table_type, .is_view=true)); CORD code = CORD_all("({ Table_t ", comprehension_name, " = {"); if (table->fallback) @@ -2485,7 +2470,7 @@ CORD compile(env_t *env, ast_t *ast) env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); const char *comprehension_name = heap_strf("set$%ld", comp_num++); ast_t *comprehension_var = FakeAST(InlineCCode, .code=CORD_all("&", comprehension_name), - .type=Type(PointerType, .pointed=set_type, .is_stack=true)); + .type=Type(PointerType, .pointed=set_type, .is_view=true)); CORD code = CORD_all("({ Table_t ", comprehension_name, " = {};"); Closure_t comp_action = {.fn=add_to_set_comprehension, .userdata=comprehension_var}; scope->comprehension_action = &comp_action; @@ -2675,7 +2660,7 @@ CORD compile(env_t *env, ast_t *ast) : compile_to_pointer_depth(env, call->self, 0, false); CORD comparison; if (call->args) { - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); + type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), .ret=Type(IntType, .bits=TYPE_IBITS32)); arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t)); @@ -2688,7 +2673,7 @@ CORD compile(env_t *env, ast_t *ast) CORD self = compile_to_pointer_depth(env, call->self, 1, false); CORD comparison; if (call->args) { - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); + type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), .ret=Type(IntType, .bits=TYPE_IBITS32)); arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t)); @@ -2699,7 +2684,7 @@ CORD compile(env_t *env, ast_t *ast) return CORD_all("Array$heapify(", self, ", ", comparison, ", ", padded_item_size, ")"); } else if (streq(call->name, "heap_push")) { CORD self = compile_to_pointer_depth(env, call->self, 1, false); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); + type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), .ret=Type(IntType, .bits=TYPE_IBITS32)); ast_t *default_cmp = FakeAST(InlineCCode, @@ -2712,7 +2697,7 @@ CORD compile(env_t *env, ast_t *ast) return CORD_all("Array$heap_push_value(", self, ", ", arg_code, ", ", padded_item_size, ")"); } else if (streq(call->name, "heap_pop")) { CORD self = compile_to_pointer_depth(env, call->self, 1, false); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); + type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), .ret=Type(IntType, .bits=TYPE_IBITS32)); ast_t *default_cmp = FakeAST(InlineCCode, @@ -2724,7 +2709,7 @@ CORD compile(env_t *env, ast_t *ast) return CORD_all("Array$heap_pop_value(", self, ", ", arg_code, ", ", padded_item_size, ", ", compile_type(item_t), ")"); } else if (streq(call->name, "binary_search")) { CORD self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); + type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *fn_t = Type(FunctionType, .args=new(arg_t, .name="x", .type=item_ptr, .next=new(arg_t, .name="y", .type=item_ptr)), .ret=Type(IntType, .bits=TYPE_IBITS32)); ast_t *default_cmp = FakeAST(InlineCCode, @@ -2746,7 +2731,7 @@ CORD compile(env_t *env, ast_t *ast) ", ", compile_type_info(env, self_value_t), ")"); } else if (streq(call->name, "first")) { CORD self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); + type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); type_t *predicate_type = Type( ClosureType, .fn=Type(FunctionType, .args=new(arg_t, .name="item", .type=item_ptr), .ret=Type(BoolType))); arg_t *arg_spec = new(arg_t, .name="predicate", .type=predicate_type); @@ -3489,7 +3474,7 @@ CORD compile_type_info(env_t *env, type_t *t) } case PointerType: { auto ptr = Match(t, PointerType); - CORD sigil = ptr->is_stack ? "&" : "@"; + CORD sigil = ptr->is_view ? "&" : "@"; return CORD_asprintf("Pointer$info(%r, %r)", CORD_quoted(sigil), compile_type_info(env, ptr->pointed)); -- cgit v1.2.3 From 63a5032ca06cd9f9bfaea6b0ea077d49a3034654 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 27 Oct 2024 20:25:16 -0400 Subject: Disallow mutation of read-only views --- compile.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'compile.c') diff --git a/compile.c b/compile.c index a0069b34..775c17df 100644 --- a/compile.c +++ b/compile.c @@ -278,9 +278,20 @@ CORD compile_type(type_t *t) static CORD compile_lvalue(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 an immutable value, you can't assign to it"); + if (ast->tag == Index) { + ast_t *subject = Match(ast, Index)->indexed; + type_t *t = get_type(env, subject); + if (t->tag == PointerType && Match(t, PointerType)->is_view) + code_err(ast, "This is a read-only view and you can't mutate its contents"); + else + code_err(subject, "This is an immutable value, you can't mutate its contents"); + } else if (ast->tag == FieldAccess) { + ast_t *subject = Match(ast, FieldAccess)->fielded; + type_t *t = get_type(env, subject); + if (t->tag == PointerType && Match(t, PointerType)->is_view) + code_err(subject, "This is a read-only view and you can't mutate its fields"); + else + code_err(subject, "This is an immutable value, you can't assign to its fields"); } else { code_err(ast, "This is a value of type %T and can't be used as an assignment target", get_type(env, ast)); } -- cgit v1.2.3