aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2026-03-13 14:07:29 +0200
committerKimplul <kimi.h.kuparinen@gmail.com>2026-03-13 14:09:53 +0200
commited7da0d9e31e8dd6847e2e603f0d1943330cf4d0 (patch)
tree16e4785bd92f43a37bed6b0028b0f239faa00d51
parent25b8db28cf87708808744b97774c2a8427e129e2 (diff)
downloadfwd-ed7da0d9e31e8dd6847e2e603f0d1943330cf4d0.tar.gz
fwd-ed7da0d9e31e8dd6847e2e603f0d1943330cf4d0.zip
add initial reference invalidation
+ Makes vec example actually memory safe, which is cool + Specify owner > sub relationships with ">" in closure parameter lists, uses the same group idea as closure calls + Relies on users implementing functions in a consistent manner, since you can kind of do whatever with pointers. Presumably there would be a stdlib of vec/map/set etc. which applications could then use and by proxy be memory safe. Although some more checks wouldn't hurt, I suppose? + Not sure I like having reference invalidation be 'just a move', seems to work alright but the semantics of it are a bit muddy.
-rw-r--r--TODO24
-rw-r--r--examples/vec.fwd2
-rw-r--r--include/fwd/ast.h10
-rw-r--r--src/analyze.c5
-rw-r--r--src/ast.c12
-rw-r--r--src/move.c14
-rw-r--r--src/parser.y1
7 files changed, 66 insertions, 2 deletions
diff --git a/TODO b/TODO
index e1c6a24..9368f6f 100644
--- a/TODO
+++ b/TODO
@@ -25,3 +25,27 @@ or the current alternative
} => ;
use (var);
+
+
++ Hmm, some kind of way to say 'this thing owns this reference' would be good.
+At the moment, I'm thinking something that parameter lists can be extended with
+ownership rules, so for example
+
+ at_vec(vec v, u64 i, (vec > &i64) ok)
+
+would return a 'new' vector and a reference into the vector. If the 'vec' is
+moved, the &i64 is marked invalidated, but the &i64 is allowed to be passed
+around as long as 'vec' is not moved.
+
+For more references, something like
+
+ hmm((owner > thing1 | thing2))
+
+is three parameters, thing1 and thing2 are both
+invalidated if owner is moved (getting two indexes from a vector to swap them?
+not sure). Not sure how complex this system should be, could there for example
+be a situation where
+
+ hmm((owner > thing1 > thing2))
+
+might make sense? For now, the parser only accepts the first form, (vec > &i64).
diff --git a/examples/vec.fwd b/examples/vec.fwd
index 6950c2a..75c5b2e 100644
--- a/examples/vec.fwd
+++ b/examples/vec.fwd
@@ -75,7 +75,7 @@ append_vec(vec v, i64 e, (vec) ok)
ok(v);
}
-at_vec(vec v, u64 i, (vec, &i64) ok)
+at_vec(vec v, u64 i, (vec > &i64) ok)
{
v => [n => n, s => s, buf => buf];
guard(i < n) => {
diff --git a/include/fwd/ast.h b/include/fwd/ast.h
index f550647..4359525 100644
--- a/include/fwd/ast.h
+++ b/include/fwd/ast.h
@@ -103,6 +103,15 @@ struct type {
/* next */
struct type *n;
+ /* used in parameter lists; this param owns the next one
+ *
+ * Is a pointer necessary here? A bool could work as well, as long as we're
+ * just doing 'this owns the next element in this list', but if we
+ * eventually support more complex relationships, like 'this owns these
+ * next params and one of the next params owns at least one more' or
+ * something along those lines, but that's an unsure TODO) */
+ struct type *or;
+
/* opt group */
long group;
@@ -727,5 +736,6 @@ static inline struct type *callable_types(struct type *t)
}
void opt_group(struct ast *a, struct ast *b);
+void own_type_group(struct type *owner, struct type *sub);
#endif /* AST_H */
diff --git a/src/analyze.c b/src/analyze.c
index c05542a..fc94e15 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -252,7 +252,12 @@ static int deduce_closure_types(struct scope *scope, struct ast *node, struct ty
return -1;
}
+ /* use same loop to deduce ownership rules */
for (; param && t; param = param->n, t = t->n) {
+ /* if this param owns the next, reflect that in our variables */
+ if (t->or)
+ param->or = param->n;
+
if (var_type(param))
continue;
diff --git a/src/ast.c b/src/ast.c
index d7d97e0..4573d3e 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -123,6 +123,8 @@ struct ast *gen_ast(enum ast_kind kind,
n->s = s;
n->v = v;
n->d = d;
+ n->or = NULL;
+ n->ol = NULL;
n->loc = loc;
return n;
}
@@ -139,6 +141,7 @@ struct type *tgen_type(enum type_kind kind,
n->k = kind;
n->t0 = t0;
n->id = id;
+ n->or = NULL;
n->loc = loc;
return n;
}
@@ -378,6 +381,7 @@ struct type *clone_type(struct type *type)
if (type->t0)
new->t0 = clone_type_list(type->t0);
+ new->or = type->or;
new->group = type->group;
new->loc = type->loc;
return new;
@@ -752,3 +756,11 @@ void opt_group(struct ast *a, struct ast *b)
a->or = b;
b->ol = a;
}
+
+void own_type_group(struct type *owner, struct type *sub)
+{
+ /* sub does not get a reference to owner so if owner is moved, only the
+ * sub is marked as used. sub is still allowed to be passed around
+ * freely */
+ owner->or = sub;
+}
diff --git a/src/move.c b/src/move.c
index 5137719..a76ffd5 100644
--- a/src/move.c
+++ b/src/move.c
@@ -372,6 +372,19 @@ static int mvcheck_id(struct state *state, struct ast *node)
if (def->k != AST_VAR_DEF)
return 0;
+ struct ast_pair *prev = find_move(state, def);
+
+ /* a reference invalidation is represented as a 'moved'
+ * reference, which is not expressible within the
+ * language but is constructed as part of
+ * opt_group_left/opt_group_right forcing a move to
+ * happen. Hack? */
+ if (def->t->k == TYPE_REF && prev) {
+ /** @todo a more fitting error message? */
+ move_error(node, prev->use);
+ return -1;
+ }
+
if (is_trivially_copyable(def->t))
return 0;
@@ -381,7 +394,6 @@ static int mvcheck_id(struct state *state, struct ast *node)
return -1;
}
- struct ast_pair *prev = find_move(state, def);
if (prev) {
/* error messages for opt groups could be improved */
move_error(node, prev->use);
diff --git a/src/parser.y b/src/parser.y
index 6210a95..776e1c9 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -289,6 +289,7 @@ type
rev_types
: rev_types "," type { $$ = $3; $$->n = $1; }
+ | rev_types ">" type { $$ = $3; $$->n = $1; own_type_group($1, $3); }
| type
types