diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2026-03-13 14:07:29 +0200 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2026-03-13 14:09:53 +0200 |
| commit | ed7da0d9e31e8dd6847e2e603f0d1943330cf4d0 (patch) | |
| tree | 16e4785bd92f43a37bed6b0028b0f239faa00d51 | |
| parent | 25b8db28cf87708808744b97774c2a8427e129e2 (diff) | |
| download | fwd-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-- | TODO | 24 | ||||
| -rw-r--r-- | examples/vec.fwd | 2 | ||||
| -rw-r--r-- | include/fwd/ast.h | 10 | ||||
| -rw-r--r-- | src/analyze.c | 5 | ||||
| -rw-r--r-- | src/ast.c | 12 | ||||
| -rw-r--r-- | src/move.c | 14 | ||||
| -rw-r--r-- | src/parser.y | 1 |
7 files changed, 66 insertions, 2 deletions
@@ -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; @@ -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; +} @@ -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 |
