aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-02-25 14:17:37 -0500
committerBruce Hill <bruce@bruce-hill.com>2024-02-25 14:17:37 -0500
commit5344789d8aed3beef2cdeaf2678cff554e29456a (patch)
tree29e4046fd2a0998b1a4e1ee5c5a2f20d57d15d80
parent7c889a4d0a57198e9b2707a44cd5f13fb2b42abc (diff)
More stringent requirements on '&' refs
-rw-r--r--typecheck.c48
1 files changed, 31 insertions, 17 deletions
diff --git a/typecheck.c b/typecheck.c
index a5153512..57dc9970 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -200,25 +200,39 @@ type_t *get_type(env_t *env, ast_t *ast)
return Type(PointerType, .is_optional=false, .pointed=pointed);
}
case StackReference: {
+ // Supported:
+ // &variable
+ // &struct_variable.field.(...)
+ // &struct_ptr.field.(...)
+ // Not supported:
+ // &ptr[]
+ // &list[index]
+ // &table[key]
+ // &(expression).field
+ // &(expression)
+ // &optional_struct_ptr.field
ast_t *value = Match(ast, StackReference)->value;
- type_t *pointed_t = get_type(env, Match(ast, StackReference)->value);
- bool is_stack = true;
- bool is_readonly = !can_be_mutated(env, value);
- // References to heap members/indexes are heap pointers, e.g. v := @Vec{1,2}; &v.x
- switch (value->tag) {
- case FieldAccess: {
- type_t *fielded_t = get_type(env, Match(value, FieldAccess)->fielded);
- is_stack = fielded_t->tag == PointerType ? Match(fielded_t, PointerType)->is_stack : true;
- break;
- }
- case Index: {
- type_t *indexed_t = get_type(env, Match(value, Index)->indexed);
- is_stack = indexed_t->tag == PointerType ? Match(indexed_t, PointerType)->is_stack : true;
- break;
- }
- default: break;
+ if (value->tag == Var)
+ return Type(PointerType, .pointed=get_type(env, value), .is_stack=true);
+
+ if (value->tag == 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 == PointerType) {
+ auto ptr = Match(base_type, PointerType);
+ if (ptr->is_optional)
+ code_err(base, "This value might be null, so it can't be safely dereferenced");
+ return Type(PointerType, .pointed=ref_type, .is_stack=ptr->is_stack, .is_readonly=ptr->is_readonly);
+ } else if (base->tag == Var) {
+ return Type(PointerType, .pointed=ref_type, .is_stack=true);
+ }
}
- return Type(PointerType, .pointed=pointed_t, .is_stack=is_stack, .is_readonly=is_readonly);
+
+ code_err(ast, "'&' stack references can only be used on variables or fields of variables");
}
case StringJoin: case StringLiteral: {
return Type(StringType);