aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-15 02:35:44 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-15 02:40:51 +0330
commit3de6ae4a6a74b2c4fa14c3f2d6fbfff3bb83c5f5 (patch)
tree577ac1025bb02a11069cf288a38bfc5120e8402d
parentdestructors: reassigning an m owner that still holds a value frees the old va... (diff)
destructors: nothing moves out of a value with a drop: pop on an array with 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 (fields were already borrowed projections); spec 6.15.5, four conformance programs
-rw-r--r--docs/SPEC.md1
-rw-r--r--src/compiler/builtins.zc9
-rw-r--r--tests/conformance/6-ownership/drop_elements_pop_ok.zc12
-rw-r--r--tests/conformance/6-ownership/drop_no_move_out_child.zc12
-rw-r--r--tests/conformance/6-ownership/drop_no_move_out_field.zc13
-rw-r--r--tests/conformance/6-ownership/drop_no_move_out_pop.zc11
-rw-r--r--tests/xref.baseline1
7 files changed, 58 insertions, 1 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md
index 8c2f5dc..dc4934a 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -997,6 +997,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.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 8c00636..44ed3b1 100644
--- a/src/compiler/builtins.zc
+++ b/src/compiler/builtins.zc
@@ -670,13 +670,18 @@ int typearg() {
}
// len(a), cap(a), push(a, x), pop(a), resize(a, n): growable arrays.
// len of a T[n] is just n.
+// arrroot: the local the array belongs to (itself, or the owner of the struct it is a field of), or -1
+int arrroot = 0 - 1;
word arrvalue(int c) {
- argloc = -1; arrloc = -1;
+ argloc = -1; arrloc = -1; arrroot = -1;
if (lv && lvk == LV_LOCAL && xty[0] == TY_DYN) {
arrloc = lvi;
if (o3on && o3cache[lvi]) argloc = lvi;
}
effect_value = effect_mask(c); rval(); plainuse(c); af_access(xaf, tpack());
+ if (arrloc >= 0) arrroot = lroot(arrloc);
+ else if (c >= C_VAR && tracked(c - C_VAR)) arrroot = lroot(c - C_VAR);
+ else if (xbor >= 0 && tracked(xbor)) arrroot = lroot(xbor);
return xty;
}
// parse an array argument and retain the receiver's mutation and bounds facts.
@@ -803,6 +808,8 @@ int array_pop(word t) {
if (isfl(xty)) xfloat = 1;
xaf = 0; xbor = -1; effect_value = 0;
if (isref(xty)) {
+ // pop moves the element out: not out of an array with a drop, whose drop may need it (6.15.5)
+ if (fchk && arrroot >= 0 && ldroppable(arrroot)) cerrn(arrroot, "can't move out of a value with drop");
xcls = C_FRESH; xaf = af_new(-1, 2, tpack()); newsz = -1; newleaf = 0; return C_FRESH;
}
return 0;
diff --git a/tests/conformance/6-ownership/drop_elements_pop_ok.zc b/tests/conformance/6-ownership/drop_elements_pop_ok.zc
new file mode 100644
index 0000000..9c97e44
--- /dev/null
+++ b/tests/conformance/6-ownership/drop_elements_pop_ok.zc
@@ -0,0 +1,12 @@
+// expect: 5 drop5 after
+// destructors 6.15.5: an array without a drop can hand out a droppable element; pop gives it to the new owner, whose block end drops it
+include rt;
+struct node { int v; void drop() { rt.put_s("drop"); rt.put_n(this.v); rt.put_s(" "); } }
+fn main() {
+ node*[?] a = rt.new(node*[?]);
+ node* x = rt.new(node); x.v = 5; a.push(x);
+ { node* y = a.pop(); rt.put_n(y.v); rt.put_s(" "); }
+ rt.put_s("after ");
+ return 0;
+}
+// zc0: skip
diff --git a/tests/conformance/6-ownership/drop_no_move_out_child.zc b/tests/conformance/6-ownership/drop_no_move_out_child.zc
new file mode 100644
index 0000000..ca4a1c9
--- /dev/null
+++ b/tests/conformance/6-ownership/drop_no_move_out_child.zc
@@ -0,0 +1,12 @@
+// expect error: a borrowed projection cannot transfer ownership; transfer its owner
+// destructors 6.15.5: a child of a value with a drop can be borrowed but not given away
+include rt;
+struct node { node* next; int v; void drop() { rt.put_s("drop "); rt.put_n(this.v); } }
+int eat(node*.d n) { rt.put_n(n.v); return 0; }
+fn main() {
+ node* a = rt.new(node); a.v = 1;
+ a.next = rt.new(node); a.next.v = 2;
+ eat(a.next);
+ return 0;
+}
+// zc0: skip
diff --git a/tests/conformance/6-ownership/drop_no_move_out_field.zc b/tests/conformance/6-ownership/drop_no_move_out_field.zc
new file mode 100644
index 0000000..e4d863c
--- /dev/null
+++ b/tests/conformance/6-ownership/drop_no_move_out_field.zc
@@ -0,0 +1,13 @@
+// expect error: b: can't move out of a value with drop
+// destructors 6.15.5: an array that is a field of a value with a drop is part of it, so pop on it is refused too
+include rt;
+struct bag { int*[?] items; void drop() { rt.put_s("bag drop\n"); } }
+fn main() {
+ bag* b = rt.new(bag);
+ b.items = rt.new(int*[?]);
+ int* x = rt.new(int); *x = 5; b.items.push(x);
+ int* y = b.items.pop();
+ rt.put_n(*y);
+ return 0;
+}
+// zc0: skip
diff --git a/tests/conformance/6-ownership/drop_no_move_out_pop.zc b/tests/conformance/6-ownership/drop_no_move_out_pop.zc
new file mode 100644
index 0000000..3ee248f
--- /dev/null
+++ b/tests/conformance/6-ownership/drop_no_move_out_pop.zc
@@ -0,0 +1,11 @@
+// expect error: a: can't move out of a value with drop
+// destructors 6.15.5: pop moves an element out, so it is refused on an array with a drop, whose drop may need the element
+include rt;
+fn main() {
+ int*[?] a = drop(rt.new(int*[?]), fn() { rt.put_s("arr drop\n"); });
+ int* x = rt.new(int); *x = 5; a.push(x);
+ int* y = a.pop();
+ rt.put_n(*y);
+ return 0;
+}
+// zc0: skip
diff --git a/tests/xref.baseline b/tests/xref.baseline
index 1660302..e83798d 100644
--- a/tests/xref.baseline
+++ b/tests/xref.baseline
@@ -25,6 +25,7 @@ argowner args
argowner_fact args
argtemporary args
arrloc arrvalue chk_reset
+arrroot arrvalue
asg_quiet assigncheck reassign_free
asgcap reserve_assignment
asm_fixes reserve_asm_references