aboutsummaryrefslogtreecommitdiff
path: root/tests/programs
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 10:47:30 +0000
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 10:47:30 +0000
commit4a22cf28742bf33577678de494aa09ab60a3c820 (patch)
treed9133c7ab431fd3f41ef7713a869d23be4539244 /tests/programs
parentMerge four small fixes: std hovers, capture terminator, bare static calls, vo... (diff)
parentzc: add the defer statement (diff)
Merge the defer statement; todo: tick it, add its follow-ups
Diffstat (limited to 'tests/programs')
-rw-r--r--tests/programs/defer.zc235
1 files changed, 235 insertions, 0 deletions
diff --git a/tests/programs/defer.zc b/tests/programs/defer.zc
new file mode 100644
index 0000000..bdf85e1
--- /dev/null
+++ b/tests/programs/defer.zc
@@ -0,0 +1,235 @@
+namespace;
+include rt;
+
+// defer: code that runs when the enclosing block ends, on every way out of it
+
+fn pn(x) { rt.put_n(x); rt.put_c(' '); return 0; }
+fn nl() { rt.put_c(10); return 0; }
+
+// several defers run in reverse order of registration
+fn order() {
+ pn(1);
+ defer pn(30);
+ defer pn(20);
+ defer { pn(10); pn(11); }
+ pn(2);
+ return 0;
+}
+
+// the deferred code sees the values the variables have when it runs
+fn late() {
+ let x = 1;
+ defer pn(x);
+ x = 2;
+ x = 7;
+ return 0;
+}
+
+// the value of return is computed before the defers run
+fn early(a) {
+ let x = a;
+ defer x = x + 100;
+ defer pn(x);
+ if (a > 5) return x * 2;
+ x = x + 1;
+ return x;
+}
+
+// a defer that is never reached registers nothing
+fn skipped(a) {
+ if (a == 0) return 0 - 1;
+ defer pn(500);
+ if (a == 1) return 1;
+ defer pn(501);
+ return 2;
+}
+
+// a defer inside an if only applies when that branch ran
+fn branch(a) {
+ defer pn(9);
+ if (a) { defer pn(1); pn(2); }
+ else { defer pn(3); pn(4); }
+ pn(5);
+ return 0;
+}
+
+// break and continue run the defers of the blocks they leave
+fn loops() {
+ let i = 0;
+ defer pn(900);
+ while (i < 5) {
+ defer pn(100 + i);
+ i++;
+ if (i == 2) continue;
+ if (i == 4) break;
+ pn(i);
+ }
+ pn(777);
+ for (let j = 0; j < 3; j++) {
+ defer { pn(200 + j); }
+ if (j == 1) { defer pn(300); continue; }
+ pn(j);
+ }
+ let k = 0;
+ do {
+ k++;
+ defer pn(400 + k);
+ if (k == 2) continue;
+ if (k == 3) break;
+ } while (k < 10);
+ return 0;
+}
+
+// the deferred code means what it meant where it was written: a variable an
+// inner block declares again does not capture its names
+fn lexical(a) {
+ let x = 1;
+ defer pn(x);
+ {
+ let x = 50;
+ defer pn(x + 1);
+ if (a) return x;
+ pn(x);
+ }
+ x = 2;
+ let y = 3;
+ return y;
+}
+
+// a defer outside the loop is not run by break or continue
+fn outer() {
+ defer pn(1000);
+ for (let i = 0; i < 3; i++) {
+ defer pn(i);
+ {
+ defer pn(50 + i);
+ if (i == 1) continue;
+ pn(60 + i);
+ }
+ if (i == 2) break;
+ }
+ pn(2000);
+ return 0;
+}
+
+// nested blocks, each with its own
+fn nest() {
+ defer pn(1);
+ {
+ defer pn(2);
+ {
+ defer pn(3);
+ pn(4);
+ }
+ pn(5);
+ }
+ pn(6);
+ return 7;
+}
+
+// switch arms are blocks; break leaves the arm and the arm's defers run
+fn arms(v) {
+ defer pn(0);
+ switch (v) {
+ case 1: defer pn(11); pn(10);
+ case 2: defer pn(21); if (v == 2) break; pn(20);
+ default: defer pn(31); return 30;
+ }
+ pn(99);
+ return 0;
+}
+
+// a block expression is a block too
+fn bexpr() {
+ let v = { defer pn(1); pn(2); 40 + 2 };
+ pn(v);
+ return 0;
+}
+
+// the owner stays usable until the block ends; the defer is the transfer
+fn owner(n) {
+ let p = rt.new(4);
+ defer rt.free(p);
+ p[0] = n;
+ p[1] = n * 2;
+ if (n == 3) return p[0] + p[1];
+ pn(p[0] + p[1]);
+ return 0;
+}
+
+fn owner_loop() {
+ for (let i = 0; i < 3; i++) {
+ let p = rt.new(2);
+ defer rt.free(p);
+ p[0] = i;
+ if (i == 1) continue;
+ pn(p[0]);
+ }
+ return 0;
+}
+
+// defer in a closure belongs to the closure
+fn closure_defer() {
+ let f = fn(a) { defer pn(a + 1); pn(a); return a * 2; };
+ defer pn(0);
+ pn(f(10));
+ return 0;
+}
+
+// unchecked code: no analysis, the deferred code still runs
+unchecked fn fn_in() {
+ defer pn(5);
+ pn(4);
+ if (1) return 0;
+ return 1;
+}
+fn unchecked_defer() {
+ unchecked {
+ defer pn(3);
+ pn(2);
+ }
+ fn_in();
+ return 0;
+}
+
+// a deferred block can have its own loops, with break and continue
+fn deferred_loop() {
+ defer {
+ for (let i = 0; i < 5; i++) {
+ if (i == 1) continue;
+ if (i == 4) break;
+ pn(i);
+ }
+ }
+ pn(9);
+ return 0;
+}
+
+fn main() {
+ order(); nl();
+ late(); nl();
+ pn(early(1)); nl();
+ pn(early(9)); nl();
+ pn(skipped(0)); nl();
+ pn(skipped(1)); nl();
+ pn(skipped(2)); nl();
+ branch(1); nl();
+ branch(0); nl();
+ loops(); nl();
+ outer(); nl();
+ pn(lexical(1)); nl();
+ pn(lexical(0)); nl();
+ pn(nest()); nl();
+ arms(1); nl();
+ arms(2); nl();
+ arms(3); nl();
+ bexpr(); nl();
+ let before = rt.live_objects();
+ pn(owner(3)); owner(4); nl();
+ owner_loop(); nl();
+ pn(rt.live_objects() - before); nl();
+ closure_defer(); nl();
+ unchecked_defer(); nl();
+ deferred_loop(); nl();
+ return 0;
+}