aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/SPEC.md2
-rw-r--r--src/compiler/builtins.zc13
-rw-r--r--src/compiler/expr.zc1
-rw-r--r--tests/data/spawn_hint.zc11
-rwxr-xr-xtests/test.sh1
5 files changed, 27 insertions, 1 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md
index dc4934a..4406123 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -996,7 +996,7 @@ let guard = drop(heap(0), fn() { unlock(); }); // unl
```
6.15.2 a value dies, and its drop runs, where 6.5 frees it: at the end of its owner's block or an early exit, at `rt.free`, when its owner is reassigned (6.8.5), or with its container, whose drop runs before its children's. a new value assigned to the owner can have its own drop set on the next statement, like a declaration's. a value with a drop that nothing holds (a temporary) dies at the end of its full statement, with hint: value with drop is never bound; it drops at the end of this statement. an owned local with a drop is not "never used" (6.5.2): holding it is its use.
6.15.3 the collector runs the drop of an unreachable value after its sweep, never during it, and at most once: the value is kept until a later collection finds it unreachable again. a drop that stores `this` somewhere reachable keeps the value alive, and its drop does not run again. there is no collection at exit, so collector-owned values are not dropped then. a freed block forgets its drop, so a block reused at the same address has none.
-6.15.4 copying a value with a drop into an actor message is error: x: has drop and can't be copied into a message.
+6.15.4 copying a value with a drop into an actor message is error: x: has drop and can't be copied into a message. `spawn` copies the whole vm into the worker process, so both processes hold an owned value that is live at the call and both run its drop: hint: x: has drop and is live at spawn; the worker's copy drops it too.
6.15.5 nothing moves out of a value with a drop, whose drop may need what it holds. a field or element of it is a borrowed projection and can't be given away (error: a borrowed projection cannot transfer ownership; transfer its owner), and `pop` on an array that has a drop, or on an array field of a value with a drop, is error: a: can't move out of a value with drop. there is no `take`. borrowing a child is fine, and an array without a drop hands out its elements with `pop` as before, drops and all.
## 7. compile-time execution
diff --git a/src/compiler/builtins.zc b/src/compiler/builtins.zc
index 44ed3b1..fb2dcb2 100644
--- a/src/compiler/builtins.zc
+++ b/src/compiler/builtins.zc
@@ -856,6 +856,19 @@ int tdroppable(word t) {
}
// local i holds a value with a drop: one set on it, or one its type declares
int ldroppable(int i) { return (local_dropset && local_dropset[i]) || tdroppable(ltype[i]); }
+// x.spawn() on a mailbox struct snapshots the whole vm into a worker process, so an
+// owned value with a drop that is live there is dropped once in each process (6.15.4)
+int spawn_hint() {
+ int i = lbase;
+ if (!fchk || !local_dropset) return 0;
+ while (i < nloc) {
+ if (!empty(lstate[i]) && !isalias(i) && lstate[i] != S_BORROW && ldroppable(i) && hint_begin(tpack(), HINT_SMALL)) {
+ rt.put_s(lname[i]); rt.put_s(": has drop and is live at spawn; the worker's copy drops it too"); dg_close();
+ }
+ i++;
+ }
+ return 0;
+}
// entering o3 from normal code: the collector waits until the outermost region is left
int o3_enter_code() { callname(intern("rt.o3_enter")); op(O_DROP); return 0; }
// a fresh value with a drop that nothing holds: it is held in a hidden slot and
diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc
index 7687394..1cd765b 100644
--- a/src/compiler/expr.zc
+++ b/src/compiler/expr.zc
@@ -1199,6 +1199,7 @@ word field(int c) {
st = xty;
if (rt.eq_w(n, "drop") && (is_op(s_lp) || is_op(s_eq)) && (isptr(st) || st == tword) && !(isptr(st) && st[4][0] == TY_STRUCT && tfindfield(st[4], n) >= 0)) return drop_member(c, borrowed);
if (rt.eq_w(n, "send") && is_op(s_lp) && isptr(st) && st[4][0] == TY_STRUCT && tfindfield(st[4], intern("__mailbox")) >= 0) send_check = 1;
+ if (rt.eq_w(n, "spawn") && is_op(s_lp) && isptr(st) && st[4][0] == TY_STRUCT && tfindfield(st[4], intern("__mailbox")) >= 0) spawn_hint();
if (st[0] == TY_ARR || st[0] == TY_DYN) {
if (pmatch) probe_member(st);
if (is_op(s_lp) && array_method(n)) { xaf = borrowed; return array_member(c, n); }
diff --git a/tests/data/spawn_hint.zc b/tests/data/spawn_hint.zc
new file mode 100644
index 0000000..b59657d
--- /dev/null
+++ b/tests/data/spawn_hint.zc
@@ -0,0 +1,11 @@
+namespace;
+include rt, actor;
+struct res { int fd; void drop() { rt.put_s("closed"); } }
+actor Echo { int total; int handle(int x) { return x; } }
+fn main() {
+ res* r = rt.new(res); r.fd = 3;
+ Echo* e = rt.new(Echo);
+ e.spawn();
+ e.close();
+ return r.fd;
+}
diff --git a/tests/test.sh b/tests/test.sh
index 9aee6d0..6dfecf1 100755
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -580,6 +580,7 @@ s_lsp() {
chk "$($lsp < examples/struct_closure_opt.zc | grep -c error)" "0" "lsp: methods and closures check clean"
case "$($lsp < tests/data/drop_hint.zc)" in *"value with drop is never bound; it drops at the end of this statement"*) chk 1 1 "lsp hint: a value with drop that is never bound";; *) chk 0 1 "lsp hint: a value with drop that is never bound";; esac
case "$($lsp < tests/data/reassign_hint.zc)" in *"p: reassigning frees the old value"*) chk 1 1 "lsp hint: reassigning an owner frees the old value";; *) chk 0 1 "lsp hint: reassigning an owner frees the old value";; esac
+ case "$($lsp < tests/data/spawn_hint.zc)" in *"r: has drop and is live at spawn; the worker's copy drops it too"*) chk 1 1 "lsp hint: a value with drop live at spawn";; *) chk 0 1 "lsp hint: a value with drop live at spawn";; esac
# a file of a larger build checks on its own: its bodyless declarations are not errors in check mode
for f in src/runtime/rt.zc src/runtime/run.zc src/lsp/server.zc; do chk "$($lsp < $f | grep -c ': error:')" "0" "lsp: $f checks alone"; done
# the compiler is several files that only make sense together: check them as one document