From 8703e2c85a8ecdea9e869e876e347ebd4cb6ef88 Mon Sep 17 00:00:00 2001 From: zinc Date: Thu, 15 Oct 2026 14:59:31 +0330 Subject: analyzer: a back link (container's owner stored into it) stays a plain reference, fixing a compile hang on a<->b and a leak; ending an owner kills its owned children too (fixes use after reassigning a container); escape fixtures --- src/compiler/analyzer.zc | 5 ++++- tests/conformance/6-ownership/back_link_alias.zc | 6 ++++++ tests/conformance/6-ownership/back_link_block_ok.zc | 6 ++++++ tests/conformance/6-ownership/back_link_cont.zc | 6 ++++++ tests/conformance/6-ownership/back_link_cycle.zc | 17 +++++++++++++++++ tests/conformance/6-ownership/back_link_free.zc | 6 ++++++ tests/conformance/6-ownership/back_link_glob.zc | 7 +++++++ tests/conformance/6-ownership/back_link_move.zc | 7 +++++++ tests/conformance/6-ownership/back_link_reasg.zc | 6 ++++++ tests/conformance/6-ownership/back_link_ret.zc | 7 +++++++ 10 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 tests/conformance/6-ownership/back_link_alias.zc create mode 100644 tests/conformance/6-ownership/back_link_block_ok.zc create mode 100644 tests/conformance/6-ownership/back_link_cont.zc create mode 100644 tests/conformance/6-ownership/back_link_cycle.zc create mode 100644 tests/conformance/6-ownership/back_link_free.zc create mode 100644 tests/conformance/6-ownership/back_link_glob.zc create mode 100644 tests/conformance/6-ownership/back_link_move.zc create mode 100644 tests/conformance/6-ownership/back_link_reasg.zc create mode 100644 tests/conformance/6-ownership/back_link_ret.zc diff --git a/src/compiler/analyzer.zc b/src/compiler/analyzer.zc index a1588e8..839c1ef 100644 --- a/src/compiler/analyzer.zc +++ b/src/compiler/analyzer.zc @@ -1009,7 +1009,7 @@ int end_owner(int i) { end_why = 0; end_whom = 0; local_ended[i] = info[2]; local_end_info[i] = info; lstate[i] = S_DEAD; while (j < nloc) { - if (j != i && isalias(j) && lroot(j) == i) { local_ended[j] = info[2]; local_end_info[j] = info; lstate[j] = S_DEAD; } + if (j != i && lstate[j] == S_CHILD && lroot(j) == i) { local_ended[j] = info[2]; local_end_info[j] = info; lstate[j] = S_DEAD; } // aliases and owned children go with it j++; } return 0; @@ -1612,6 +1612,9 @@ int assigncheck(int k, int i, int rc) { touch(i); if (rc == C_FRESH) { emitown(i); return C_PLAIN; } if (j < 0) return C_PLAIN; + // a back link: storing the container's own owner (or the container itself) + // into it. it stays a plain reference; ownership would form a cycle. + if (r == j) { touch(j); return C_PLAIN; } js = lstate[j]; if (js == S_CHILD) cerrn(j, "already belongs to a container"); else if (js == S_BORROW) cerrn(j, "is borrowed and can't be stored"); diff --git a/tests/conformance/6-ownership/back_link_alias.zc b/tests/conformance/6-ownership/back_link_alias.zc new file mode 100644 index 0000000..58db710 --- /dev/null +++ b/tests/conformance/6-ownership/back_link_alias.zc @@ -0,0 +1,6 @@ +// expect error: b: is an owned object and can't be copied into another variable +// spec 6.6 +include rt; +struct node { int value; node* prev; node* next; }; +fn main() { node* p; { node* a = rt.new(node); node* b = rt.new(node); a.next = b; b.prev = a; p = b; } rt.put_n(p.prev.value); } +// zc0: skip diff --git a/tests/conformance/6-ownership/back_link_block_ok.zc b/tests/conformance/6-ownership/back_link_block_ok.zc new file mode 100644 index 0000000..7955081 --- /dev/null +++ b/tests/conformance/6-ownership/back_link_block_ok.zc @@ -0,0 +1,6 @@ +// expect: 40 +// spec 6.6 +include rt; +struct node { int value; node* prev; node* next; }; +fn main() { memdebug on; { node* a = rt.new(node); node* b = rt.new(node); a.next = b; b.prev = a; a.value = 4; rt.put_n(b.prev.value); } rt.put_n(rt.memreport()); } +// zc0: skip diff --git a/tests/conformance/6-ownership/back_link_cont.zc b/tests/conformance/6-ownership/back_link_cont.zc new file mode 100644 index 0000000..d8083fd --- /dev/null +++ b/tests/conformance/6-ownership/back_link_cont.zc @@ -0,0 +1,6 @@ +// expect error: b: already belongs to a container +// spec 6.6 +include rt; +struct node { int value; node* prev; node* next; }; +fn main() { node* c = rt.new(node); node* a = rt.new(node); node* b = rt.new(node); a.next = b; b.prev = a; c.next = b; rt.put_n(c.value); } +// zc0: skip diff --git a/tests/conformance/6-ownership/back_link_cycle.zc b/tests/conformance/6-ownership/back_link_cycle.zc new file mode 100644 index 0000000..fc47100 --- /dev/null +++ b/tests/conformance/6-ownership/back_link_cycle.zc @@ -0,0 +1,17 @@ +// expect: 0/ +// spec 6.6 +include rt; +struct node { int value; node* prev; node* next; }; +fn main() { + memdebug on; + for (int round = 0; round < 50; round++) { + node* a = rt.new(node); + node* b = rt.new(node); + a.next = b; + b.prev = a; + a.value = round; + b.value = b.prev.value; + } + rt.put_n(rt.memreport()); rt.put_c(10); +} +// zc0: skip diff --git a/tests/conformance/6-ownership/back_link_free.zc b/tests/conformance/6-ownership/back_link_free.zc new file mode 100644 index 0000000..a8fb5e8 --- /dev/null +++ b/tests/conformance/6-ownership/back_link_free.zc @@ -0,0 +1,6 @@ +// expect error: b: used after it was given away; b borrows a, and a was freed +// spec 6.6 +include rt; +struct node { int value; node* prev; node* next; }; +fn main() { node* a = rt.new(node); node* b = rt.new(node); a.next = b; b.prev = a; rt.free(a); rt.put_n(b.prev.value); } +// zc0: skip diff --git a/tests/conformance/6-ownership/back_link_glob.zc b/tests/conformance/6-ownership/back_link_glob.zc new file mode 100644 index 0000000..ecba2e5 --- /dev/null +++ b/tests/conformance/6-ownership/back_link_glob.zc @@ -0,0 +1,7 @@ +// expect error: b: is not an owner, so it can't be stored in a global +// spec 6.6 +include rt; +struct node { int value; node* prev; node* next; }; +node* keep; +fn main() { node* a = rt.new(node); node* b = rt.new(node); a.next = b; b.prev = a; keep = b; } +// zc0: skip diff --git a/tests/conformance/6-ownership/back_link_move.zc b/tests/conformance/6-ownership/back_link_move.zc new file mode 100644 index 0000000..47490d6 --- /dev/null +++ b/tests/conformance/6-ownership/back_link_move.zc @@ -0,0 +1,7 @@ +// expect error: a was stored in a global or untracked memory +// spec 6.6 +include rt; +struct node { int value; node* prev; node* next; }; +node* keep2; +fn main() { node* a = rt.new(node); node* b = rt.new(node); a.next = b; b.prev = a; keep2 = a; rt.put_n(b.prev.value); } +// zc0: skip diff --git a/tests/conformance/6-ownership/back_link_reasg.zc b/tests/conformance/6-ownership/back_link_reasg.zc new file mode 100644 index 0000000..02709a8 --- /dev/null +++ b/tests/conformance/6-ownership/back_link_reasg.zc @@ -0,0 +1,6 @@ +// expect error: b: used after it was given away; b borrows a, and a was freed by reassigning it +// spec 6.6 +include rt; +struct node { int value; node* prev; node* next; }; +fn main() { node* a = rt.new(node); node* b = rt.new(node); a.next = b; b.prev = a; a = rt.new(node); a.value = 1; rt.put_n(b.prev.value); } +// zc0: skip diff --git a/tests/conformance/6-ownership/back_link_ret.zc b/tests/conformance/6-ownership/back_link_ret.zc new file mode 100644 index 0000000..ee10fce --- /dev/null +++ b/tests/conformance/6-ownership/back_link_ret.zc @@ -0,0 +1,7 @@ +// expect error: b: has to be u to be returned +// spec 6.6 +include rt; +struct node { int value; node* prev; node* next; }; +node* g() { node* a = rt.new(node); node* b = rt.new(node); a.next = b; b.prev = a; return b; } +fn main() { node* x = g(); rt.put_n(x.value); } +// zc0: skip -- cgit v1.2.3