diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-12-18 14:44:37 -0500 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-12-18 14:44:37 -0500 |
| commit | c6c7cc727290663e50b8a4d28e92214fa805ca95 (patch) | |
| tree | a4f77690d44fedd445178a701969101030790a06 | |
| parent | aa262344712be27afc02441a309ddde03fa2bec9 (diff) | |
Revert "Deprecate "&" for stack references"
This reverts commit 41c0ea851a542bcd7d54b8c5c06d70e1e00095e1.
| -rw-r--r-- | ast.c | 5 | ||||
| -rw-r--r-- | ast.h | 6 | ||||
| -rw-r--r-- | compile.c | 59 | ||||
| -rw-r--r-- | parse.c | 41 | ||||
| -rw-r--r-- | repl.c | 2 | ||||
| -rw-r--r-- | test/arrays.tm | 2 | ||||
| -rw-r--r-- | test/tables.tm | 4 | ||||
| -rw-r--r-- | typecheck.c | 72 | ||||
| -rw-r--r-- | types.c | 12 | ||||
| -rw-r--r-- | types.h | 4 |
10 files changed, 141 insertions, 66 deletions
@@ -115,6 +115,7 @@ CORD ast_to_xml(ast_t *ast) T(Negative, "<Negative>%r</Negative>", ast_to_xml(data.value)) T(Not, "<Not>%r</Not>", ast_to_xml(data.value)) T(HeapAllocate, "<HeapAllocate>%r</HeapAllocate>", ast_to_xml(data.value)) + T(StackReference, "<StackReference>%r</StackReference>", ast_to_xml(data.value)) T(Min, "<Min>%r%r%r</Min>", ast_to_xml(data.lhs), ast_to_xml(data.rhs), optional_tagged("key", data.key)) T(Max, "<Max>%r%r%r</Max>", ast_to_xml(data.lhs), ast_to_xml(data.rhs), optional_tagged("key", data.key)) T(Array, "<Array>%r%r</Array>", optional_tagged_type("item-type", data.item_type), ast_list_to_xml(data.items)) @@ -177,8 +178,8 @@ CORD type_ast_to_xml(type_ast_t *t) #define T(type, ...) case type: { auto data = t->__data.type; (void)data; return CORD_asprintf(__VA_ARGS__); } T(UnknownTypeAST, "<UnknownType/>") T(VarTypeAST, "%s", data.name) - T(PointerTypeAST, "<PointerType is_view=\"%s\">%r</PointerType>", - data.is_view ? "yes" : "no", type_ast_to_xml(data.pointed)) + T(PointerTypeAST, "<PointerType is_stack=\"%s\">%r</PointerType>", + data.is_stack ? "yes" : "no", type_ast_to_xml(data.pointed)) T(ArrayTypeAST, "<ArrayType>%r</ArrayType>", type_ast_to_xml(data.item)) T(SetTypeAST, "<TableType>%r</TableType>", type_ast_to_xml(data.item)) T(ChannelTypeAST, "<ChannelType>%r</ChannelType>", type_ast_to_xml(data.item)) @@ -99,7 +99,7 @@ struct type_ast_s { } VarTypeAST; struct { type_ast_t *pointed; - bool is_view:1; + bool is_stack:1; } PointerTypeAST; struct { type_ast_t *item; @@ -128,7 +128,7 @@ typedef enum { TextLiteral, TextJoin, PrintStatement, Declare, Assign, BinaryOp, UpdateAssign, - Not, Negative, HeapAllocate, + Not, Negative, HeapAllocate, StackReference, Min, Max, Array, Channel, Set, Table, TableEntry, Comprehension, FunctionDef, Lambda, @@ -194,7 +194,7 @@ struct ast_s { } BinaryOp, UpdateAssign; struct { ast_t *value; - } Not, Negative, HeapAllocate; + } Not, Negative, HeapAllocate, StackReference; struct { ast_t *lhs, *rhs, *key; } Min, Max; @@ -315,18 +315,11 @@ static CORD compile_lvalue(env_t *env, ast_t *ast) if (!can_be_mutated(env, ast)) { 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"); + 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 %T value, you can't assign to its fields", t); + code_err(subject, "This is an immutable %T value, you can't assign to its fields", t); } else { code_err(ast, "This is a value of type %T and can't be used as an assignment target", get_type(env, ast)); } @@ -374,7 +367,7 @@ static CORD compile_lvalue(env_t *env, ast_t *ast) code_err(ast, "Table indexes cannot be unchecked"); return CORD_all("*(", compile_type(Type(PointerType, value_type)), ")Table$reserve(", compile_to_pointer_depth(env, index->indexed, 1, false), ", ", - compile_to_type(env, index->index, Type(PointerType, key_type, .is_view=true)), ", NULL,", + compile_to_type(env, index->index, Type(PointerType, key_type, .is_stack=true)), ", NULL,", compile_type_info(env, container_t), ")"); } else { code_err(ast, "I don't know how to assign to this target"); @@ -595,6 +588,8 @@ CORD compile_statement(env_t *env, ast_t *ast) if (assign->targets->ast->tag == Index && lhs_t->tag == OptionalType && value_type(get_type(env, Match(assign->targets->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; + if (has_stack_memory(lhs_t)) + code_err(test->expr, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); env_t *val_scope = with_enum_scope(env, lhs_t); CORD value = compile_to_type(val_scope, assign->values->ast, lhs_t); return CORD_asprintf( @@ -618,6 +613,8 @@ CORD compile_statement(env_t *env, ast_t *ast) if (target->ast->tag == Index && lhs_t->tag == OptionalType && value_type(get_type(env, Match(target->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; + if (has_stack_memory(lhs_t)) + code_err(ast, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); if (target == assign->targets) first_type = lhs_t; env_t *val_scope = with_enum_scope(env, lhs_t); @@ -699,6 +696,8 @@ CORD compile_statement(env_t *env, ast_t *ast) if (assign->targets->ast->tag == Index && lhs_t->tag == OptionalType && value_type(get_type(env, Match(assign->targets->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; + if (has_stack_memory(lhs_t)) + code_err(ast, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); env_t *val_env = with_enum_scope(env, lhs_t); CORD val = compile_to_type(val_env, assign->values->ast, lhs_t); return CORD_all(compile_assignment(env, assign->targets->ast, val), ";\n"); @@ -711,6 +710,8 @@ CORD compile_statement(env_t *env, ast_t *ast) if (target->ast->tag == Index && lhs_t->tag == OptionalType && value_type(get_type(env, Match(target->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; + if (has_stack_memory(lhs_t)) + code_err(ast, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); env_t *val_env = with_enum_scope(env, lhs_t); CORD val = compile_to_type(val_env, value->ast, lhs_t); CORD_appendf(&code, "%r $%ld = %r;\n", compile_type(lhs_t), i++, val); @@ -1603,7 +1604,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_view=true); + t = Type(PointerType, .pointed=t, .is_stack=true); ++depth; } else { auto ptr = Match(t, PointerType); @@ -1636,7 +1637,7 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t) type_t *actual = get_type(env, ast); // Promote values to views-of-values if needed: - if (t->tag == PointerType && Match(t, PointerType)->is_view && actual->tag != PointerType) + if (t->tag == PointerType && Match(t, PointerType)->is_stack && actual->tag != PointerType) return CORD_all("stack(", compile_to_type(env, ast, Match(t, PointerType)->pointed), ")"); CORD code = compile(env, ast); @@ -2105,6 +2106,13 @@ 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 + return CORD_all("stack(", compile(env, subject), ")"); + } case Optional: { ast_t *value = Match(ast, Optional)->value; CORD value_code = compile(env, value); @@ -2537,7 +2545,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_view=true)); + .type=Type(PointerType, .pointed=array_type, .is_stack=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, " = {};"); @@ -2619,7 +2627,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_view=true)); + .type=Type(PointerType, .pointed=table_type, .is_stack=true)); CORD code = CORD_all("({ Table_t ", comprehension_name, " = {"); if (table->fallback) @@ -2673,7 +2681,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_view=true)); + .type=Type(PointerType, .pointed=set_type, .is_stack=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; @@ -2748,6 +2756,9 @@ CORD compile(env_t *env, ast_t *ast) CORD def = "typedef struct {"; for (int64_t i = 1; i <= Table$length(*closed_vars); i++) { struct { const char *name; binding_t *b; } *entry = Table$entry(*closed_vars, i); + if (has_stack_memory(entry->b->type)) + code_err(ast, "This function is holding onto a reference to %T stack memory in the variable `%s`, but the function may outlive the stack memory", + entry->b->type, entry->name); if (entry->b->type->tag == ModuleType) continue; set_binding(body_scope, entry->name, new(binding_t, .type=entry->b->type, .code=CORD_cat("userdata->", entry->name))); @@ -2818,8 +2829,8 @@ CORD compile(env_t *env, ast_t *ast) CORD self = compile(env, call->self); #define EXPECT_POINTER(article, name) do { \ - if (pointer_depth < 1) code_err(call->self, "I expected "article" "name" @pointer here, not "article" "name" value"); \ - else if (pointer_depth > 1) code_err(call->self, "I expected "article" "name" @pointer here, not a nested "name" pointer"); \ + if (pointer_depth < 1) code_err(call->self, "I expected "article" "name" pointer here, not "article" "name" value"); \ + else if (pointer_depth > 1) code_err(call->self, "I expected "article" "name" pointer here, not a nested "name" pointer"); \ } while (0) switch (self_value_t->tag) { case ArrayType: { @@ -2884,7 +2895,7 @@ CORD compile(env_t *env, ast_t *ast) self = compile_to_pointer_depth(env, call->self, 0, false); CORD comparison; if (call->args) { - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); + 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)); arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t)); @@ -2897,7 +2908,7 @@ CORD compile(env_t *env, ast_t *ast) EXPECT_POINTER("an", "array"); CORD comparison; if (call->args) { - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); + 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)); arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t)); @@ -2908,7 +2919,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")) { EXPECT_POINTER("an", "array"); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); + 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)); ast_t *default_cmp = FakeAST(InlineCCode, @@ -2921,7 +2932,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")) { EXPECT_POINTER("an", "array"); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); + 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)); ast_t *default_cmp = FakeAST(InlineCCode, @@ -2933,7 +2944,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")) { self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); + 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)); ast_t *default_cmp = FakeAST(InlineCCode, @@ -2955,7 +2966,7 @@ CORD compile(env_t *env, ast_t *ast) ", ", compile_type_info(env, self_value_t), ")"); } else if (streq(call->name, "first")) { self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_view=true); + 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))); arg_t *arg_spec = new(arg_t, .name="predicate", .type=predicate_type); @@ -3811,7 +3822,7 @@ CORD compile_type_info(env_t *env, type_t *t) } case PointerType: { auto ptr = Match(t, PointerType); - CORD sigil = ptr->is_view ? "&" : "@"; + CORD sigil = ptr->is_stack ? "&" : "@"; return CORD_asprintf("Pointer$info(%r, %r)", CORD_quoted(sigil), compile_type_info(env, ptr->pointed)); @@ -127,6 +127,7 @@ static PARSER(parse_return); static PARSER(parse_say); static PARSER(parse_set); static PARSER(parse_skip); +static PARSER(parse_stack_reference); static PARSER(parse_statement); static PARSER(parse_stop); static PARSER(parse_struct_def); @@ -626,18 +627,18 @@ type_ast_t *parse_channel_type(parse_ctx_t *ctx, const char *pos) { type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) { const char *start = pos; - bool is_view; + bool is_stack; if (match(&pos, "@")) - is_view = false; + is_stack = false; else if (match(&pos, "&")) - is_view = true; + is_stack = true; else return NULL; spaces(&pos); type_ast_t *type = expect(ctx, start, &pos, parse_non_optional_type, "I couldn't parse a pointer type after this point"); - type_ast_t *ptr_type = NewTypeAST(ctx->file, start, pos, PointerTypeAST, .pointed=type, .is_view=is_view); + type_ast_t *ptr_type = NewTypeAST(ctx->file, start, pos, PointerTypeAST, .pointed=type, .is_stack=is_stack); spaces(&pos); while (match(&pos, "?")) ptr_type = NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type=ptr_type); @@ -1262,6 +1263,32 @@ PARSER(parse_heap_alloc) { return ast; } +PARSER(parse_stack_reference) { + const char *start = pos; + if (!match(&pos, "&")) return NULL; + spaces(&pos); + ast_t *val = expect(ctx, start, &pos, parse_term_no_suffix, "I expected an expression for this '&'"); + + for (;;) { + ast_t *new_term; + if ((new_term=parse_index_suffix(ctx, val)) + || (new_term=parse_fncall_suffix(ctx, val)) + || (new_term=parse_field_suffix(ctx, val))) { + val = new_term; + } else break; + } + pos = val->end; + + ast_t *ast = NewAST(ctx->file, start, pos, StackReference, .value=val); + for (;;) { + ast_t *next = parse_optional_suffix(ctx, ast); + if (!next) next = parse_non_optional_suffix(ctx, ast); + if (!next) break; + ast = next; + } + return ast; +} + PARSER(parse_not) { const char *start = pos; if (!match_word(&pos, "not")) return NULL; @@ -1595,6 +1622,7 @@ PARSER(parse_term_no_suffix) { || (term=parse_int(ctx, pos)) || (term=parse_negative(ctx, pos)) // Must come after num/int || (term=parse_heap_alloc(ctx, pos)) + || (term=parse_stack_reference(ctx, pos)) || (term=parse_bool(ctx, pos)) || (term=parse_text(ctx, pos)) || (term=parse_path(ctx, pos)) @@ -2356,11 +2384,6 @@ PARSER(parse_doctest) { if (!match(&pos, ">>")) return NULL; spaces(&pos); ast_t *expr = expect(ctx, start, &pos, parse_statement, "I couldn't parse the expression for this doctest"); - spaces(&pos); - comment(&pos); - // We need at least one newline before looking for "= <expected value>" - if (strspn(pos, "\r\n") < 1 && strcspn(pos, "\r\n") > 0) - parser_err(ctx, pos, pos + strcspn(pos, "\r\n"), "I couldn't parse this code as part of the doctest expression"); whitespace(&pos); const char* output = NULL; if (match(&pos, "=")) { @@ -132,7 +132,7 @@ const TypeInfo_t *type_to_type_info(type_t *t) } case PointerType: { auto ptr = Match(t, PointerType); - CORD sigil = ptr->is_view ? "&" : "@"; + CORD sigil = ptr->is_stack ? "&" : "@"; const TypeInfo_t *pointed_info = type_to_type_info(ptr->pointed); const TypeInfo_t pointer_info = *Pointer$info(sigil, pointed_info); return memcpy(GC_MALLOC(sizeof(TypeInfo_t)), &pointer_info, sizeof(TypeInfo_t)); diff --git a/test/arrays.tm b/test/arrays.tm index f59ed6ce..8bc5a7f6 100644 --- a/test/arrays.tm +++ b/test/arrays.tm @@ -71,7 +71,7 @@ func main(): = @[999, 20, 30] do: - >> arr := [10, 20, 30] + >> arr := &[10, 20, 30] >> reversed := arr:reversed() = [30, 20, 10] # Ensure the copy-on-write behavior triggers: diff --git a/test/tables.tm b/test/tables.tm index 403f25e2..e67c7127 100644 --- a/test/tables.tm +++ b/test/tables.tm @@ -82,9 +82,9 @@ func main(): = 10 do: - >> t4 := {"one": 1} + >> t4 := &{"one": 1} >> t4["one"] = 999 >> t4["two"] = 222 >> t4 - = {"one":999, "two":222} + = &{"one":999, "two":222} diff --git a/typecheck.c b/typecheck.c index 4cdc683d..b87e5248 100644 --- a/typecheck.c +++ b/typecheck.c @@ -47,13 +47,13 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) type_t *pointed_t = parse_type_ast(env, ptr->pointed); if (pointed_t->tag == VoidType) code_err(ast, "Void pointers are not supported. You probably meant 'Memory' instead of 'Void'"); - return Type(PointerType, .pointed=pointed_t, .is_view=ptr->is_view); + return Type(PointerType, .pointed=pointed_t, .is_stack=ptr->is_stack); } case ArrayTypeAST: { type_ast_t *item_type = Match(ast, ArrayTypeAST)->item; type_t *item_t = parse_type_ast(env, item_type); if (!item_t) code_err(item_type, "I can't figure out what this type is."); - if (has_view_memory(item_t)) + if (has_stack_memory(item_t)) code_err(item_type, "Arrays can't have stack references because the array may outlive the stack frame."); if (type_size(item_t) > ARRAY_MAX_STRIDE) code_err(ast, "This array holds items that take up %ld bytes, but the maximum supported size is %ld bytes. Consider using an array of pointers instead.", @@ -64,7 +64,7 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) type_ast_t *item_type = Match(ast, SetTypeAST)->item; type_t *item_t = parse_type_ast(env, item_type); if (!item_t) code_err(item_type, "I can't figure out what this type is."); - if (has_view_memory(item_t)) + if (has_stack_memory(item_t)) code_err(item_type, "Sets can't have stack references because the array may outlive the stack frame."); if (type_size(item_t) > ARRAY_MAX_STRIDE) code_err(ast, "This set holds items that take up %ld bytes, but the maximum supported size is %ld bytes. Consider using an set of pointers instead.", @@ -87,18 +87,21 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) type_ast_t *key_type_ast = table_type->key; type_t *key_type = parse_type_ast(env, key_type_ast); if (!key_type) code_err(key_type_ast, "I can't figure out what type this is."); - if (has_view_memory(key_type)) + if (has_stack_memory(key_type)) code_err(key_type_ast, "Tables can't have stack references because the array may outlive the stack frame."); if (table_type->value) { type_t *val_type = parse_type_ast(env, table_type->value); if (!val_type) code_err(table_type->value, "I can't figure out what type this is."); - if (has_view_memory(val_type)) + if (has_stack_memory(val_type)) code_err(table_type->value, "Tables can't have stack references because the array may outlive the stack frame."); else if (val_type->tag == OptionalType) code_err(ast, "Tables with optional-typed values are not currently supported"); return Type(TableType, .key_type=key_type, .value_type=val_type); } else if (table_type->default_value) { - return Type(TableType, .key_type=key_type, .default_value=table_type->default_value); + type_t *t = Type(TableType, .key_type=key_type, .default_value=table_type->default_value); + if (has_stack_memory(t)) + code_err(ast, "Tables can't have stack references because the array may outlive the stack frame."); + return t; } else { code_err(ast, "No value type or default value!"); } @@ -106,7 +109,7 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) case FunctionTypeAST: { auto fn = Match(ast, FunctionTypeAST); type_t *ret_t = fn->ret ? parse_type_ast(env, fn->ret) : Type(VoidType); - if (has_view_memory(ret_t)) + if (has_stack_memory(ret_t)) code_err(fn->ret, "Functions are not allowed to return stack references, because the reference may no longer exist on the stack."); arg_t *type_args = NULL; for (arg_ast_t *arg = fn->args; arg; arg = arg->next) { @@ -455,7 +458,7 @@ type_t *get_function_def_type(env_t *env, ast_t *ast) REVERSE_LIST(args); type_t *ret = fn->ret_type ? parse_type_ast(scope, fn->ret_type) : Type(VoidType); - if (has_view_memory(ret)) + if (has_stack_memory(ret)) code_err(ast, "Functions can't return stack references because the reference may outlive its stack frame."); return Type(FunctionType, .args=args, .ret=ret); } @@ -534,10 +537,49 @@ type_t *get_type(env_t *env, ast_t *ast) } case HeapAllocate: { type_t *pointed = get_type(env, Match(ast, HeapAllocate)->value); - if (has_view_memory(pointed)) + if (has_stack_memory(pointed)) code_err(ast, "Stack references cannot be moved to the heap because they may outlive the stack frame they were created in."); return Type(PointerType, .pointed=pointed); } + case StackReference: { + // Supported: + // &variable + // &struct_variable.field.(...) + // &struct_ptr.field.(...) + // &[10, 20, 30]; &{key:value}; &{10, 20, 30} + // &Foo(...) + // &(expression) + // Not supported: + // &ptr[] + // &list[index] + // &table[key] + // &(expression).field + // &optional_struct_ptr.field + ast_t *value = Match(ast, StackReference)->value; + switch (value->tag) { + case FieldAccess: { + ast_t *base = value; + while (base->tag == FieldAccess) + base = Match(base, FieldAccess)->fielded; + + type_t *ref_type = get_type(env, value); + type_t *base_type = get_type(env, base); + if (base_type->tag == OptionalType) { + code_err(base, "This value might be null, so it can't be safely dereferenced"); + } else if (base_type->tag == PointerType) { + auto ptr = Match(base_type, PointerType); + return Type(PointerType, .pointed=ref_type, .is_stack=ptr->is_stack); + } else if (base->tag == Var) { + return Type(PointerType, .pointed=ref_type, .is_stack=true); + } + code_err(ast, "'&' stack references can only be used on the fields of pointers and local variables"); + } + case Index: + code_err(ast, "'&' stack references are not supported for array or table indexing"); + default: + return Type(PointerType, .pointed=get_type(env, value), .is_stack=true); + } + } case Optional: { ast_t *value = Match(ast, Optional)->value; type_t *t = get_type(env, value); @@ -596,7 +638,7 @@ type_t *get_type(env_t *env, ast_t *ast) } else { code_err(ast, "I can't figure out what type this array has because it has no members or explicit type"); } - if (has_view_memory(item_type)) + if (has_stack_memory(item_type)) code_err(ast, "Arrays cannot hold stack references, because the array may outlive the stack frame the reference was created in."); return Type(ArrayType, .item_type=item_type); } @@ -625,7 +667,7 @@ type_t *get_type(env_t *env, ast_t *ast) item_type = item_merged; } } - if (has_view_memory(item_type)) + if (has_stack_memory(item_type)) code_err(ast, "Sets cannot hold stack references because the set may outlive the reference's stack frame."); return Type(SetType, .item_type=item_type); } @@ -673,7 +715,7 @@ type_t *get_type(env_t *env, ast_t *ast) value_type = val_merged; } } - if (has_view_memory(key_type) || has_view_memory(value_type)) + if (has_stack_memory(key_type) || has_stack_memory(value_type)) code_err(ast, "Tables cannot hold stack references because the table may outlive the reference's stack frame."); return Type(TableType, .key_type=key_type, .value_type=value_type, .default_value=table->default_value); } @@ -1135,7 +1177,7 @@ type_t *get_type(env_t *env, ast_t *ast) declared, ret); } - if (has_view_memory(ret)) + if (has_stack_memory(ret)) code_err(ast, "Functions can't return stack references because the reference may outlive its stack frame."); return Type(ClosureType, Type(FunctionType, .args=args, .ret=ret)); } @@ -1324,9 +1366,7 @@ PUREFUNC bool can_be_mutated(env_t *env, ast_t *ast) case Index: { auto index = Match(ast, Index); type_t *indexed_type = get_type(env, index->indexed); - if (indexed_type->tag == PointerType) - return !Match(indexed_type, PointerType)->is_view; - return can_be_mutated(env, index->indexed); + return (indexed_type->tag == PointerType); } default: return false; } @@ -74,7 +74,7 @@ CORD type_to_cord(type_t *t) { } case PointerType: { auto ptr = Match(t, PointerType); - CORD sigil = ptr->is_view ? "&" : "@"; + CORD sigil = ptr->is_stack ? "&" : "@"; return CORD_all(sigil, type_to_cord(ptr->pointed)); } case EnumType: { @@ -134,7 +134,7 @@ bool type_is_a(type_t *t, type_t *req) auto t_ptr = Match(t, PointerType); auto req_ptr = Match(req, PointerType); if (type_eq(t_ptr->pointed, req_ptr->pointed)) - return (!t_ptr->is_view && req_ptr->is_view) || (!t_ptr->is_view); + return (!t_ptr->is_stack && req_ptr->is_stack) || (!t_ptr->is_stack); } return false; } @@ -295,11 +295,11 @@ PUREFUNC bool can_send_over_channel(type_t *t) } } -PUREFUNC bool has_view_memory(type_t *t) +PUREFUNC bool has_stack_memory(type_t *t) { switch (t->tag) { - case PointerType: return Match(t, PointerType)->is_view; - case OptionalType: return has_view_memory(Match(t, OptionalType)->type); + case PointerType: return Match(t, PointerType)->is_stack; + case OptionalType: return has_stack_memory(Match(t, OptionalType)->type); default: return false; } } @@ -378,7 +378,7 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed) // Can't use @Foo for a function that wants @Baz // But you *can* use @Foo for a function that wants @Memory return false; - else if (actual_ptr->is_view && !needed_ptr->is_view) + else if (actual_ptr->is_stack && !needed_ptr->is_stack) // Can't use &x for a function that wants a @Foo or ?Foo return false; else @@ -102,7 +102,7 @@ struct type_s { } ClosureType; struct { type_t *pointed; - bool is_view:1; + bool is_stack:1; } PointerType; struct { const char *name; @@ -144,7 +144,7 @@ type_t *value_type(type_t *a); typedef enum {NUM_PRECISION_EQUAL, NUM_PRECISION_LESS, NUM_PRECISION_MORE, NUM_PRECISION_INCOMPARABLE} precision_cmp_e; PUREFUNC precision_cmp_e compare_precision(type_t *a, type_t *b); PUREFUNC bool has_heap_memory(type_t *t); -PUREFUNC bool has_view_memory(type_t *t); +PUREFUNC bool has_stack_memory(type_t *t); PUREFUNC bool can_send_over_channel(type_t *t); PUREFUNC bool can_promote(type_t *actual, type_t *needed); PUREFUNC const char *enum_single_value_tag(type_t *enum_type, type_t *t); |
