aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ast.c5
-rw-r--r--ast.h6
-rw-r--r--compile.c59
-rw-r--r--parse.c41
-rw-r--r--repl.c2
-rw-r--r--test/arrays.tm2
-rw-r--r--test/tables.tm4
-rw-r--r--typecheck.c72
-rw-r--r--types.c12
-rw-r--r--types.h4
10 files changed, 141 insertions, 66 deletions
diff --git a/ast.c b/ast.c
index 421f806e..bfd15357 100644
--- a/ast.c
+++ b/ast.c
@@ -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))
diff --git a/ast.h b/ast.h
index 82d4de03..56876665 100644
--- a/ast.h
+++ b/ast.h
@@ -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;
diff --git a/compile.c b/compile.c
index f17e796b..77397b11 100644
--- a/compile.c
+++ b/compile.c
@@ -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));
diff --git a/parse.c b/parse.c
index 1cd51bd6..d63822c1 100644
--- a/parse.c
+++ b/parse.c
@@ -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, "=")) {
diff --git a/repl.c b/repl.c
index 801a16d9..b2cca300 100644
--- a/repl.c
+++ b/repl.c
@@ -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;
}
diff --git a/types.c b/types.c
index f526af60..bcbb74c9 100644
--- a/types.c
+++ b/types.c
@@ -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
diff --git a/types.h b/types.h
index c496c391..0163c4dc 100644
--- a/types.h
+++ b/types.h
@@ -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);