aboutsummaryrefslogtreecommitdiff
path: root/docs/history/15-o3/code/tests
diff options
context:
space:
mode:
authorGravatar Moon Flower <moonflower@elhenney.local> 2026-10-12 08:46:23 +0330
committerGravatar Moon Flower <moonflower@elhenney.local> 2026-10-12 08:46:23 +0330
commitecc350ca5c2eb50be8309c5f14f8bf0d87db9a02 (patch)
tree70e0af76a1b13affff7c6cc8aac7b3e6ef960478 /docs/history/15-o3/code/tests
a
Diffstat (limited to 'docs/history/15-o3/code/tests')
-rw-r--r--docs/history/15-o3/code/tests/arrays.zc24
-rw-r--r--docs/history/15-o3/code/tests/basics.zc22
-rw-r--r--docs/history/15-o3/code/tests/bench.zc30
-rw-r--r--docs/history/15-o3/code/tests/borrowmove.zc2
-rw-r--r--docs/history/15-o3/code/tests/buddy.zc15
-rw-r--r--docs/history/15-o3/code/tests/enhance.zc29
-rw-r--r--docs/history/15-o3/code/tests/errors/back_to_m.zc2
-rw-r--r--docs/history/15-o3/code/tests/errors/borrow_escape.zc3
-rw-r--r--docs/history/15-o3/code/tests/errors/branch_mismatch.zc3
-rw-r--r--docs/history/15-o3/code/tests/errors/child_dies.zc3
-rw-r--r--docs/history/15-o3/code/tests/errors/child_recast.zc2
-rw-r--r--docs/history/15-o3/code/tests/errors/copy_owned.zc2
-rw-r--r--docs/history/15-o3/code/tests/errors/d_into_block.zc2
-rw-r--r--docs/history/15-o3/code/tests/errors/d_kept.zc2
-rw-r--r--docs/history/15-o3/code/tests/errors/discard_u.zc3
-rw-r--r--docs/history/15-o3/code/tests/errors/give_m.zc3
-rw-r--r--docs/history/15-o3/code/tests/errors/global_store.zc3
-rw-r--r--docs/history/15-o3/code/tests/errors/leak_overwrite.zc2
-rw-r--r--docs/history/15-o3/code/tests/errors/lend_d.zc4
-rw-r--r--docs/history/15-o3/code/tests/errors/loop_move.zc3
-rw-r--r--docs/history/15-o3/code/tests/errors/plain_return.zc3
-rw-r--r--docs/history/15-o3/code/tests/errors/return_m.zc3
-rw-r--r--docs/history/15-o3/code/tests/errors/tag_mismatch.zc2
-rw-r--r--docs/history/15-o3/code/tests/errors/u_dropped.zc2
-rw-r--r--docs/history/15-o3/code/tests/errors/u_skip_level.zc2
-rw-r--r--docs/history/15-o3/code/tests/errors/unchecked_touch.zc2
-rw-r--r--docs/history/15-o3/code/tests/errors/unused.zc2
-rw-r--r--docs/history/15-o3/code/tests/errors/use_after_give.zc3
-rw-r--r--docs/history/15-o3/code/tests/gc.zc20
-rw-r--r--docs/history/15-o3/code/tests/joins.zc32
-rw-r--r--docs/history/15-o3/code/tests/langerrors/const_index.zc2
-rw-r--r--docs/history/15-o3/code/tests/langerrors/fixed_nolen.zc2
-rw-r--r--docs/history/15-o3/code/tests/langerrors/fixed_push.zc2
-rw-r--r--docs/history/15-o3/code/tests/langerrors/fixed_to_dyn.zc4
-rw-r--r--docs/history/15-o3/code/tests/langerrors/o3_addr.zc2
-rw-r--r--docs/history/15-o3/code/tests/langerrors/o3_asm.zc2
-rw-r--r--docs/history/15-o3/code/tests/langerrors/o3_let.zc2
-rw-r--r--docs/history/15-o3/code/tests/langerrors/o3_param.zc3
-rw-r--r--docs/history/15-o3/code/tests/manyerrors.zc6
-rw-r--r--docs/history/15-o3/code/tests/meta.zc46
-rw-r--r--docs/history/15-o3/code/tests/misc.zc30
-rw-r--r--docs/history/15-o3/code/tests/o3.zc83
-rw-r--r--docs/history/15-o3/code/tests/o3_bounds.zc2
-rw-r--r--docs/history/15-o3/code/tests/overflow_data.zc1
-rw-r--r--docs/history/15-o3/code/tests/overflow_frame.zc2
-rw-r--r--docs/history/15-o3/code/tests/overflow_return.zc2
-rwxr-xr-xdocs/history/15-o3/code/tests/runerr.sh10
-rw-r--r--docs/history/15-o3/code/tests/typeerrors/arg.zc5
-rw-r--r--docs/history/15-o3/code/tests/typeerrors/arith.zc3
-rw-r--r--docs/history/15-o3/code/tests/typeerrors/assign.zc3
-rw-r--r--docs/history/15-o3/code/tests/typeerrors/call.zc3
-rw-r--r--docs/history/15-o3/code/tests/typeerrors/deref.zc3
-rw-r--r--docs/history/15-o3/code/tests/typeerrors/field.zc3
-rw-r--r--docs/history/15-o3/code/tests/typeerrors/generic.zc4
-rw-r--r--docs/history/15-o3/code/tests/typeerrors/notstruct.zc2
-rw-r--r--docs/history/15-o3/code/tests/typeerrors/ret.zc4
-rw-r--r--docs/history/15-o3/code/tests/typeerrors/voidret.zc4
-rw-r--r--docs/history/15-o3/code/tests/typeoff.zc2
-rw-r--r--docs/history/15-o3/code/tests/typeok.zc21
-rw-r--r--docs/history/15-o3/code/tests/types.zc56
-rw-r--r--docs/history/15-o3/code/tests/umd.zc51
61 files changed, 595 insertions, 0 deletions
diff --git a/docs/history/15-o3/code/tests/arrays.zc b/docs/history/15-o3/code/tests/arrays.zc
new file mode 100644
index 0000000..f8d46d8
--- /dev/null
+++ b/docs/history/15-o3/code/tests/arrays.zc
@@ -0,0 +1,24 @@
+// growable arrays and fixed ones
+struct point { int x, y; };
+fn main()
+{
+ let before = live_objects();
+ {
+ []int a = new([]int);
+ for (int i = 0; i < 100; i++) push(a, i * i);
+ putn(len(a)); putc(' '); putn(cap(a)); putc(' '); putn(a[99]); putc(' ');
+ putn(pop(a)); putc(' '); putn(len(a)); putc(' ');
+ resize(a, 3); putn(len(a)); putc(' '); putn(a[2]); putc(' ');
+ fixed []int f = new(fixed []int, 5);
+ f[4] = 7; putn(f[4] + len(f)); putc(' ');
+ []point ps = new([]point, 2);
+ ps[1].y = 9; resize(ps, 10); putn(ps[1].y + len(ps)); putc(' ');
+ [8]int st; putn(len(st)); putc(' ');
+ []*point owned = new([]*point);
+ for (int i = 0; i < 20; i++) { *point p = new(point); p.x = i; push(owned, d*p); }
+ putn(owned[19].x); putc(' ');
+ putn(live_objects() - before); putc(10);
+ }
+ putn(live_objects() - before); putc(10);
+ return 0;
+}
diff --git a/docs/history/15-o3/code/tests/basics.zc b/docs/history/15-o3/code/tests/basics.zc
new file mode 100644
index 0000000..c1f1faf
--- /dev/null
+++ b/docs/history/15-o3/code/tests/basics.zc
@@ -0,0 +1,22 @@
+let table[10];
+let greeting = "hey";
+const N = 10, BIG = 0x100 * 2;
+fn fib(n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); }
+fn twice(f, x) { return f(f(x)); }
+fn inc(x) { return x + 1; }
+fn main()
+{
+ let i, s = 0, a[5];
+ for (i = 0; i < N; i++) table[i] = i * i;
+ for (let j = 0; j < N; j++) s += table[j];
+ putn(s); putc(' '); putn(fib(15)); putc(' '); putn(twice(inc, 40)); putc(' '); putn(BIG); putc(10);
+ puts(greeting); putc(' '); putn('A'); putc(' '); putn(-17 / 5); putc(' '); putn(-17 % 5); putc(' '); putn(1 << 10); putc(' '); putn(-64 >> 3); putc(10);
+ i = 0; while (1) { i++; if (i == 3) continue; if (i > 6) break; putn(i); } putc(10);
+ a[0] = 5; a[1] = a[0]++; a[2] = ++a[0]; putn(a[0]); putn(a[1]); putn(a[2]); putc(10);
+ let b = { let t = 3; t * t };
+ putn(b); putc(' '); putn(3 > 2 && 2 > 1); putn(0 || 0); putn(!5); putn(~0); putn(5 != 5); putn(4 >= 4); putc(10);
+ let x = asm { 2 3 + };
+ putn(x); putc(10);
+ asm { 'o' . 'k' . 10 . }
+ return 0;
+}
diff --git a/docs/history/15-o3/code/tests/bench.zc b/docs/history/15-o3/code/tests/bench.zc
new file mode 100644
index 0000000..a7e53f5
--- /dev/null
+++ b/docs/history/15-o3/code/tests/bench.zc
@@ -0,0 +1,30 @@
+// four kernels with and without o3; bench.sh runs each one on each backend
+int csum(int lim) { int s = 0; for (int n = 1; n < lim; n++) { int x = n; while (x != 1) { if (x & 1) x = 3 * x + 1; else x = x / 2; s++; } } return s; }
+o3 int csum3(int lim) { int s = 0; for (int n = 1; n < lim; n++) { int x = n; while (x != 1) { if (x & 1) x = 3 * x + 1; else x = x / 2; s++; } } return s; }
+int sieve(fixed []int f) { int c = 0, n = len(f); for (int i = 0; i < n; i++) f[i] = 1; for (int i = 2; i < n; i++) if (f[i]) { c++; for (int j = i + i; j < n; j += i) f[j] = 0; } return c; }
+o3 int sieve3(fixed []int f) { int c = 0, n = len(f); for (int i = 0; i < n; i++) f[i] = 1; for (int i = 2; i < n; i++) if (f[i]) { c++; for (int j = i + i; j < n; j += i) f[j] = 0; } return c; }
+int digits(int n) { int s = 0; for (int i = 0; i < n; i++) { int x = i; while (x) { s = s + x % 10; x = x / 10; } } return s; }
+o3 int digits3(int n) { int s = 0; for (int i = 0; i < n; i++) { int x = i; while (x) { s = s + x % 10; x = x / 10; } } return s; }
+int fib(int n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); }
+o3 int fib3(int n) { if (n < 2) return n; return fib3(n - 1) + fib3(n - 2); }
+// input: a letter (the kernel; z runs none) and how many times to run it
+fn main()
+{
+ fixed []int f = new(fixed []int, 50000);
+ let which = getc(), reps = 0, ch = getc(), r = 0;
+ while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = getc(); }
+ while (reps > 0) {
+ if (which == 'a') r = csum(30000);
+ if (which == 'b') r = csum3(30000);
+ if (which == 'c') r = sieve(f);
+ if (which == 'd') r = sieve3(f);
+ if (which == 'e') r = digits(300000);
+ if (which == 'f') r = digits3(300000);
+ if (which == 'g') r = fib(22);
+ if (which == 'h') r = fib3(22);
+ reps--;
+ }
+ putn(r); putc(10);
+ free(d*f);
+ return 0;
+}
diff --git a/docs/history/15-o3/code/tests/borrowmove.zc b/docs/history/15-o3/code/tests/borrowmove.zc
new file mode 100644
index 0000000..43f27b2
--- /dev/null
+++ b/docs/history/15-o3/code/tests/borrowmove.zc
@@ -0,0 +1,2 @@
+fn put(.m box, int v) { *int c = new(int); *c = v; box[0] = c; return 0; }
+fn main() { let b = new(1); put(b, 6); putn(*(b[0] as *int)); putc(10); return 0; }
diff --git a/docs/history/15-o3/code/tests/buddy.zc b/docs/history/15-o3/code/tests/buddy.zc
new file mode 100644
index 0000000..2000598
--- /dev/null
+++ b/docs/history/15-o3/code/tests/buddy.zc
@@ -0,0 +1,15 @@
+// the buddy allocator: 20000 small blocks, freed, merge back into one big one
+let keep[20000];
+fn main()
+{
+ let i = 0, p;
+ while (i < 20000) { keep[i] = new(2); i++; } // 20000 8-word blocks
+ putn(largest_free()); putc(' ');
+ i = 0; while (i < 20000) { free(keep[i]); i++; }
+ putn(largest_free()); putc(' ');
+ p = new(200000); // only possible if the small blocks merged back
+ putn(osize(p) >= 200000); putc(' ');
+ free(d*p);
+ putn(live_objects()); putc(10);
+ return 0;
+}
diff --git a/docs/history/15-o3/code/tests/enhance.zc b/docs/history/15-o3/code/tests/enhance.zc
new file mode 100644
index 0000000..33eb85b
--- /dev/null
+++ b/docs/history/15-o3/code/tests/enhance.zc
@@ -0,0 +1,29 @@
+// custom opcodes: recipes, lifted vm code, several outputs, and a fallback
+enhance clampadd(a, b) micro "a = add a b; a = min a 100; a = max a 0";
+enhance cube(x) vm { 0 $ 0 $ * * };
+enhance 2 divmod(a, b) micro "r2 = sdiv a b" "\n r1 = srem a b\n r0 = r2";
+enhance div7(a) micro "a = sdiv a 7";
+enhance urem10(a) micro "a = urem a 10";
+enhance hi(a, b) micro "a = mulhu a b";
+enhance noisy(x) vm { 0 $ . } else soft_noisy; // i/o can't be lifted, so calls use the fallback
+fn soft_noisy(x) { return x + 1; }
+let qq, rr, seed = 7;
+fn rnd() { seed = seed * 1103515245 + 12345; return seed ^ (seed >> 11); }
+fn main()
+{
+ let i = 0, a, bad = 0;
+ putn(clampadd(70, 50)); putc(' '); putn(clampadd(-50, 2)); putc(' '); putn(cube(7)); putc(' ');
+ asm { 17 5 divmod rr ! qq ! }
+ putn(qq); putc(','); putn(rr); putc(' ');
+ putn(noisy(4)); putc(' ');
+ putn(hi(-1, -1)); putc(' ');
+ while (i < 5000) {
+ a = rnd();
+ if (div7(a) != zdiv(a, 7)) bad++;
+ if (urem10(a) < 0 || urem10(a) > 9) bad++;
+ if (a / (i + 1) != zdiv(a, i + 1)) bad++;
+ i++;
+ }
+ putn(bad); putc(10);
+ return 0;
+}
diff --git a/docs/history/15-o3/code/tests/errors/back_to_m.zc b/docs/history/15-o3/code/tests/errors/back_to_m.zc
new file mode 100644
index 0000000..b8c160f
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/back_to_m.zc
@@ -0,0 +1,2 @@
+// expect: x: can't be recast back to m
+fn main() { let x = new(3); d*x; m*x; return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/borrow_escape.zc b/docs/history/15-o3/code/tests/errors/borrow_escape.zc
new file mode 100644
index 0000000..ce24480
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/borrow_escape.zc
@@ -0,0 +1,3 @@
+// expect: p: is owned and can't be returned
+fn f(.m p) { return p; }
+fn main() { let x = new(1); f(x); return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/branch_mismatch.zc b/docs/history/15-o3/code/tests/errors/branch_mismatch.zc
new file mode 100644
index 0000000..bd19968
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/branch_mismatch.zc
@@ -0,0 +1,3 @@
+// expect: x: ends the branches of an if in different states
+fn take(.d p) { return p[0]; }
+fn main() { let x = new(1); if (x[0]) take(d*x); else u*x; return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/child_dies.zc b/docs/history/15-o3/code/tests/errors/child_dies.zc
new file mode 100644
index 0000000..67caca9
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/child_dies.zc
@@ -0,0 +1,3 @@
+// expect: c: used after it was given away
+fn take(.d p) { return p[0]; }
+fn main() { let p = new(2), c = new(1); p[0] = c; take(d*p); return c[0]; }
diff --git a/docs/history/15-o3/code/tests/errors/child_recast.zc b/docs/history/15-o3/code/tests/errors/child_recast.zc
new file mode 100644
index 0000000..c9ccd62
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/child_recast.zc
@@ -0,0 +1,2 @@
+// expect: c: belongs to a container; recast the container instead
+fn main() { let p = new(2), c = new(1); p[0] = c; d*c; return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/copy_owned.zc b/docs/history/15-o3/code/tests/errors/copy_owned.zc
new file mode 100644
index 0000000..862c7ef
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/copy_owned.zc
@@ -0,0 +1,2 @@
+// expect: x: is an owned object and can't be copied
+fn main() { let x = new(1); let y = x; return y[0]; }
diff --git a/docs/history/15-o3/code/tests/errors/d_into_block.zc b/docs/history/15-o3/code/tests/errors/d_into_block.zc
new file mode 100644
index 0000000..2d7f526
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/d_into_block.zc
@@ -0,0 +1,2 @@
+// expect: x: used after it was given away
+fn main() { let x = new(1); d*x; { x[0] = 5; } return x[0]; }
diff --git a/docs/history/15-o3/code/tests/errors/d_kept.zc b/docs/history/15-o3/code/tests/errors/d_kept.zc
new file mode 100644
index 0000000..dcbfb86
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/d_kept.zc
@@ -0,0 +1,2 @@
+// expect: x: d object was never given away
+fn main() { let x = new(3); d*x; x[0] = 1; return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/discard_u.zc b/docs/history/15-o3/code/tests/errors/discard_u.zc
new file mode 100644
index 0000000..9293378
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/discard_u.zc
@@ -0,0 +1,3 @@
+// expect: can't be thrown away
+.u mk() { let p = new(1); return u*p; }
+fn main() { mk(); return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/give_m.zc b/docs/history/15-o3/code/tests/errors/give_m.zc
new file mode 100644
index 0000000..cd67896
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/give_m.zc
@@ -0,0 +1,3 @@
+// expect: x: is m; write d*x
+fn take(.d p) { return p[0]; }
+fn main() { let x = new(3); take(x); return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/global_store.zc b/docs/history/15-o3/code/tests/errors/global_store.zc
new file mode 100644
index 0000000..dc0e546
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/global_store.zc
@@ -0,0 +1,3 @@
+// expect: x: must be d to be stored in a global
+let g;
+fn main() { let x = new(1); g = x; return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/leak_overwrite.zc b/docs/history/15-o3/code/tests/errors/leak_overwrite.zc
new file mode 100644
index 0000000..d19b5fd
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/leak_overwrite.zc
@@ -0,0 +1,2 @@
+// expect: x: still owns an object
+fn main() { let x = new(1); x = new(2); return x[0]; }
diff --git a/docs/history/15-o3/code/tests/errors/lend_d.zc b/docs/history/15-o3/code/tests/errors/lend_d.zc
new file mode 100644
index 0000000..55ac7e0
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/lend_d.zc
@@ -0,0 +1,4 @@
+// expect: x: is d; pass it to a d parameter
+fn look(.m p) { return p[0]; }
+fn take(.d p) { return p[0]; }
+fn main() { let x = new(3); d*x; look(x); take(x); return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/loop_move.zc b/docs/history/15-o3/code/tests/errors/loop_move.zc
new file mode 100644
index 0000000..df3c94a
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/loop_move.zc
@@ -0,0 +1,3 @@
+// expect: x: is given away inside a loop that comes back around to it
+fn take(.d p) { return p[0]; }
+fn main() { let x = new(1); let i = 0; d*x; while (i < 3) { take(x); i++; } return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/plain_return.zc b/docs/history/15-o3/code/tests/errors/plain_return.zc
new file mode 100644
index 0000000..45fb545
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/plain_return.zc
@@ -0,0 +1,3 @@
+// expect: returns a new object; give the function a .u return type
+fn mk() { return new(1); }
+fn main() { return mk(); }
diff --git a/docs/history/15-o3/code/tests/errors/return_m.zc b/docs/history/15-o3/code/tests/errors/return_m.zc
new file mode 100644
index 0000000..b5ef519
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/return_m.zc
@@ -0,0 +1,3 @@
+// expect: x: has to be u to be returned
+.u mk() { let x = new(1); return x; }
+fn main() { let y = mk(); return y[0]; }
diff --git a/docs/history/15-o3/code/tests/errors/tag_mismatch.zc b/docs/history/15-o3/code/tests/errors/tag_mismatch.zc
new file mode 100644
index 0000000..079f249
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/tag_mismatch.zc
@@ -0,0 +1,2 @@
+// expect: c: its tag doesn't match its container's
+fn main() { let p = new(2), c = new(1); u*p; p[0] = c; return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/u_dropped.zc b/docs/history/15-o3/code/tests/errors/u_dropped.zc
new file mode 100644
index 0000000..e1ca68a
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/u_dropped.zc
@@ -0,0 +1,2 @@
+// expect: x: u object must leave this block
+fn main() { let x = new(3); u*x; return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/u_skip_level.zc b/docs/history/15-o3/code/tests/errors/u_skip_level.zc
new file mode 100644
index 0000000..6caeeb3
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/u_skip_level.zc
@@ -0,0 +1,2 @@
+// expect: t: is u; it can only be handed to a variable of the enclosing block
+fn main() { let r; { { let t = new(1); u*t; r = t; } } return r[0]; }
diff --git a/docs/history/15-o3/code/tests/errors/unchecked_touch.zc b/docs/history/15-o3/code/tests/errors/unchecked_touch.zc
new file mode 100644
index 0000000..141639d
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/unchecked_touch.zc
@@ -0,0 +1,2 @@
+// expect: x: is tracked by the analyzer and can't be used in unchecked code
+fn main() { let x = new(1); unchecked { x[0] = 1; } return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/unused.zc b/docs/history/15-o3/code/tests/errors/unused.zc
new file mode 100644
index 0000000..64aff1f
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/unused.zc
@@ -0,0 +1,2 @@
+// expect: x: m object is never used
+fn main() { let x = new(3); return 0; }
diff --git a/docs/history/15-o3/code/tests/errors/use_after_give.zc b/docs/history/15-o3/code/tests/errors/use_after_give.zc
new file mode 100644
index 0000000..8a10922
--- /dev/null
+++ b/docs/history/15-o3/code/tests/errors/use_after_give.zc
@@ -0,0 +1,3 @@
+// expect: x: used after it was given away
+fn take(.d p) { return p[0]; }
+fn main() { let x = new(3); take(d*x); return x[0]; }
diff --git a/docs/history/15-o3/code/tests/gc.zc b/docs/history/15-o3/code/tests/gc.zc
new file mode 100644
index 0000000..41f14a0
--- /dev/null
+++ b/docs/history/15-o3/code/tests/gc.zc
@@ -0,0 +1,20 @@
+let keep;
+unchecked fn churn(n)
+{
+ let i = 0, p;
+ while (i < n) { p = new(10); p[0] = i; if (i % 100 == 0) keep = p; i++; }
+ return 0;
+}
+fn main()
+{
+ let base = live_objects(), f;
+ churn(1000);
+ putn(live_objects() - base); putc(' ');
+ f = gc();
+ putn(f >= 990); putc(' '); // conservative: a stale word or two may keep one alive
+ putn(keep[0]); putc(' '); // the one a global still points at survives
+ churn(200000); // 3.2m words through a 393k word heap: needs the automatic collector
+ putn(gc_runs > 1); putc(' ');
+ putn(keep[0]); putc(10);
+ return 0;
+}
diff --git a/docs/history/15-o3/code/tests/joins.zc b/docs/history/15-o3/code/tests/joins.zc
new file mode 100644
index 0000000..913929b
--- /dev/null
+++ b/docs/history/15-o3/code/tests/joins.zc
@@ -0,0 +1,32 @@
+// joins: something given away on one path is dead afterwards, and the paths
+// that still own it free it on the way in. nothing here leaks.
+fn take(.d p) { return p[0]; }
+fn later(.d p); // not needed any more, but still allowed
+*int.u mk(int v) { *int p = new(int); *p = v; return u*p; }
+fn count(int n) { let s = 0; switch (n % 4) { case 0: s = 10; case 1, 2: s = 20; break; default: s = 30; } return s; }
+fn main()
+{
+ let base = live_objects(), i = 0, hits = 0, k;
+ while (i < 50) {
+ let x = mk(i);
+ if (i % 3 == 0) take(d*x); // else: freed here by the fixup
+ i++;
+ }
+ putn(live_objects() - base); putc(' ');
+ // give away and break: fine now; the normal exit frees it
+ i = 0;
+ let y = mk(7);
+ while (i < 10) { if (i == 4) { take(d*y); break; } i++; }
+ putn(live_objects() - base); putc(' ');
+ let z = mk(5);
+ while (1) { if (z[0] == 5) { hits = take(d*z); break; } }
+ putn(hits); putc(' ');
+ putn(count(0) + count(1) + count(2) + count(3)); putc(' ');
+ k = i > 3 ? i * 2 : 0 - 1;
+ putn(k); putc(' ');
+ putn(forward(new(int))); putc(' '); // tagged and defined below: the prescan knows .d
+ **int pp; *int q = new(int); *q = 9; pp = &q; putn(**pp); free(d*q); putc(' ');
+ putn(live_objects() - base); putc(10);
+ return 0;
+}
+fn forward(*int.d p) { return 3 + *p; }
diff --git a/docs/history/15-o3/code/tests/langerrors/const_index.zc b/docs/history/15-o3/code/tests/langerrors/const_index.zc
new file mode 100644
index 0000000..03e927b
--- /dev/null
+++ b/docs/history/15-o3/code/tests/langerrors/const_index.zc
@@ -0,0 +1,2 @@
+// expect: the index is out of range for this array
+fn main() { [4]int a; a[4] = 1; return 0; }
diff --git a/docs/history/15-o3/code/tests/langerrors/fixed_nolen.zc b/docs/history/15-o3/code/tests/langerrors/fixed_nolen.zc
new file mode 100644
index 0000000..b79c31e
--- /dev/null
+++ b/docs/history/15-o3/code/tests/langerrors/fixed_nolen.zc
@@ -0,0 +1,2 @@
+// expect: a fixed array needs its length
+fn main() { fixed []int f = new(fixed []int); free(d*f); return 0; }
diff --git a/docs/history/15-o3/code/tests/langerrors/fixed_push.zc b/docs/history/15-o3/code/tests/langerrors/fixed_push.zc
new file mode 100644
index 0000000..a19bcb4
--- /dev/null
+++ b/docs/history/15-o3/code/tests/langerrors/fixed_push.zc
@@ -0,0 +1,2 @@
+// expect: a fixed array can't grow or shrink
+fn main() { fixed []int f = new(fixed []int, 3); push(f, 1); free(d*f); return 0; }
diff --git a/docs/history/15-o3/code/tests/langerrors/fixed_to_dyn.zc b/docs/history/15-o3/code/tests/langerrors/fixed_to_dyn.zc
new file mode 100644
index 0000000..483f62f
--- /dev/null
+++ b/docs/history/15-o3/code/tests/langerrors/fixed_to_dyn.zc
@@ -0,0 +1,4 @@
+// expect: type error: argument: expected []int, got fixed []int
+typecheck on;
+int take([]int.m g) { return len(g); }
+fn main() { fixed []int f = new(fixed []int, 3); take(f); free(d*f); return 0; }
diff --git a/docs/history/15-o3/code/tests/langerrors/o3_addr.zc b/docs/history/15-o3/code/tests/langerrors/o3_addr.zc
new file mode 100644
index 0000000..815253d
--- /dev/null
+++ b/docs/history/15-o3/code/tests/langerrors/o3_addr.zc
@@ -0,0 +1,2 @@
+// expect: an o3 variable has no address
+fn main() { o3 int z = 2; *int q = &z; return *q; }
diff --git a/docs/history/15-o3/code/tests/langerrors/o3_asm.zc b/docs/history/15-o3/code/tests/langerrors/o3_asm.zc
new file mode 100644
index 0000000..c8afb44
--- /dev/null
+++ b/docs/history/15-o3/code/tests/langerrors/o3_asm.zc
@@ -0,0 +1,2 @@
+// expect: asm can't reach an o3 variable
+fn main() { o3 int zz = 2; asm { zz @ _ } return 0; }
diff --git a/docs/history/15-o3/code/tests/langerrors/o3_let.zc b/docs/history/15-o3/code/tests/langerrors/o3_let.zc
new file mode 100644
index 0000000..d2aff9a
--- /dev/null
+++ b/docs/history/15-o3/code/tests/langerrors/o3_let.zc
@@ -0,0 +1,2 @@
+// expect: o3 variables need a type
+fn main() { o3 { let x = 1; x++; } return 0; }
diff --git a/docs/history/15-o3/code/tests/langerrors/o3_param.zc b/docs/history/15-o3/code/tests/langerrors/o3_param.zc
new file mode 100644
index 0000000..5694720
--- /dev/null
+++ b/docs/history/15-o3/code/tests/langerrors/o3_param.zc
@@ -0,0 +1,3 @@
+// expect: o3 parameters need a type
+o3 int f(x) { return x; }
+fn main() { return f(1); }
diff --git a/docs/history/15-o3/code/tests/manyerrors.zc b/docs/history/15-o3/code/tests/manyerrors.zc
new file mode 100644
index 0000000..f4c0c68
--- /dev/null
+++ b/docs/history/15-o3/code/tests/manyerrors.zc
@@ -0,0 +1,6 @@
+// several errors in one run: a syntax error, an analyzer error, a type error
+typecheck on;
+fn f() { let a = ; return 0; }
+fn g() { let x = new(3); return 0; }
+int h() { *char s = 5; return 1; }
+fn main() { return 0 }
diff --git a/docs/history/15-o3/code/tests/meta.zc b/docs/history/15-o3/code/tests/meta.zc
new file mode 100644
index 0000000..bc89454
--- /dev/null
+++ b/docs/history/15-o3/code/tests/meta.zc
@@ -0,0 +1,46 @@
+fn ipow(a, b) { let r = 1; while (b > 0) { r *= a; b--; } return r; }
+// a parse word that adds an operator to the compiler's tables
+immediate fn pow_op() { next(); add_binop("**", 11, "ipow c"); return 0; }
+pow_op
+
+// unless (cond) stmt: a parse word that emits code itself
+immediate fn unless()
+{
+ let a;
+ next();
+ expect(intern("(")); rval(); plainuse(expr()); rval(); expect(intern(")"));
+ emitseq(useq[0], ulen[0]); // not
+ a = hole(); op(O_JZ); // skip the body when the condition was true
+ substmt();
+ setlit(a, here);
+ return 0;
+}
+// repeat N { body }: a macro, built from token lists
+immediate fn repeat()
+{
+ let n, body, i = 0;
+ next(); n = tv; next();
+ body = grab();
+ while (i < n) { feed(qdup(body)); i++; }
+ free(d*body);
+ return 0;
+}
+fn main()
+{
+ putn(2 ** 10); putc(' '); putn(3 + 2 ** 3 * 2); putc(10);
+ unless (1 > 2) puts("unless ok\n");
+ unless (2 > 1) puts("wrong\n");
+ repeat 3 { putc('r'); }
+ putc(10);
+ let q = quote { putn(40 + 2); putc(10); return 7; };
+ putn(q[0]); putc(' ');
+ putn(eval(q)); putc(10);
+ let f = compile(q);
+ putn(f()); putc(10);
+ free(d*q);
+ let defs = quote { fn sq(x) { return x * x; } };
+ load(defs);
+ free(d*defs);
+ putn(evals("return sq(12);")); putc(10);
+ return 0;
+}
diff --git a/docs/history/15-o3/code/tests/misc.zc b/docs/history/15-o3/code/tests/misc.zc
new file mode 100644
index 0000000..25fc53b
--- /dev/null
+++ b/docs/history/15-o3/code/tests/misc.zc
@@ -0,0 +1,30 @@
+// ct { ... } runs a block while compiling and leaves its result as a constant
+immediate fn ct()
+{
+ let q, v;
+ next();
+ q = grab();
+ v = eval(q);
+ free(d*q);
+ lit(v);
+ return 0;
+}
+// teach the asm syntax that p means out, like the old repl demo
+immediate fn teach() { next(); mnem[12] = 'p'; return 0; }
+teach
+
+fn countdown()
+{
+ asm { 5 :top 0 $ '0' + p 1 - 0 $ done z top j :done _ 10 p }
+ return 0;
+}
+fn main()
+{
+ putn(ct { let i = 0, s = 0; while (i < 10) { s += i; i++; } return s; }); putc(10);
+ countdown();
+ puts(lazy()); putc(10);
+ return 0;
+}
+// the analyzer is off from here on
+unchecked;
+fn lazy() { let p = new(3); p[0] = 'o'; p[1] = 'k'; return p; }
diff --git a/docs/history/15-o3/code/tests/o3.zc b/docs/history/15-o3/code/tests/o3.zc
new file mode 100644
index 0000000..296d1d5
--- /dev/null
+++ b/docs/history/15-o3/code/tests/o3.zc
@@ -0,0 +1,83 @@
+// o3 regions: every kind of control flow, against the same code without o3
+struct pt { int x; int y; };
+int g = 3;
+int sq(int x) { return x * x; }
+o3 int collatz(int n)
+{
+ int steps = 0;
+ while (n != 1) { if (n % 2 == 0) n = n / 2; else n = 3 * n + 1; steps++; }
+ return steps;
+}
+o3 int ack(int am, int n)
+{
+ if (am == 0) return n + 1;
+ if (n == 0) return ack(am - 1, 1);
+ return ack(am - 1, ack(am, n - 1));
+}
+o3 int many(int a, int b, int c)
+{
+ int dd = a + b, e = b + c, f = c + a, h = dd * e, i2 = e * f, j = f * dd, k = h + i2 + j, l = k / 7;
+ for (int t = 0; t < 5; t++) { dd += t; e -= t; f = f ^ t; l = l + dd * e - f; }
+ return dd + e + f + h + i2 + j + k + l;
+}
+o3 int mix(int n)
+{
+ int acc = 0;
+ for (int i = 0; i < n; i++) {
+ if (i % 3 == 0 && i % 5 == 0) acc += 15;
+ else if (i % 3 == 0 || i > 90) acc += 3;
+ else acc = acc + (i & 1 ? sq(i) : g);
+ switch (i % 4) { case 0: acc += 1; case 1: acc -= 2; default: acc = acc ^ 1; }
+ if (acc > 100000) break;
+ if (i == 7) continue;
+ acc = acc + i / 10 - i % 7 + (i << 2) - (i >> 1);
+ }
+ return acc;
+}
+o3 int dyn(int n)
+{
+ []int a = new([]int, n);
+ fixed []int f = new(fixed []int, n);
+ int s = 0;
+ for (int i = 0; i < len(a); i++) { a[i] = i * i; f[i] = n - i; }
+ for (int i = 0; i < n; i++) s = s + a[i] * f[i];
+ free(d*a); free(d*f);
+ return s;
+}
+o3 int pts(int n)
+{
+ *pt p = new(pt);
+ int s = 0;
+ p->x = 1; p->y = 2;
+ for (int i = 0; i < n; i++) { p->x = p->x + p->y; p->y = p->x - p->y; s = s + p->x % 1000; }
+ free(d*p);
+ return s;
+}
+o3 int divs(int x)
+{
+ int s = 0;
+ for (int i = -50; i < 50; i++) s = s + (x + i) / 7 + (x + i) % 7 + (x - i) / -3 + i / 16 + i % 16;
+ return s;
+}
+fn main()
+{
+ o3 int lone = 2;
+ let r = 0;
+ lone = lone * 21;
+ o3 {
+ int i = 0, s = 0;
+ while (i < 100) { s = s + collatz(i + 1); i++; }
+ putn(s); putc(' ');
+ putn(lone); putc(' ');
+ lone++;
+ }
+ putn(lone); putc(' ');
+ putn(ack(2, 3)); putc(' ');
+ putn(many(1, 2, 3)); putc(' ');
+ putn(mix(100)); putc(' ');
+ putn(dyn(50)); putc(' ');
+ putn(pts(30)); putc(' ');
+ putn(divs(1000)); putc(' ');
+ putn(divs(-77)); putc(10);
+ return 0;
+}
diff --git a/docs/history/15-o3/code/tests/o3_bounds.zc b/docs/history/15-o3/code/tests/o3_bounds.zc
new file mode 100644
index 0000000..fb347c0
--- /dev/null
+++ b/docs/history/15-o3/code/tests/o3_bounds.zc
@@ -0,0 +1,2 @@
+// an index outside a []T inside o3 code: caught by the inline check
+fn main() { []int a = new([]int, 5); o3 { int i = 7; a[i] = 1; } free(d*a); return 0; }
diff --git a/docs/history/15-o3/code/tests/overflow_data.zc b/docs/history/15-o3/code/tests/overflow_data.zc
new file mode 100644
index 0000000..3042f0a
--- /dev/null
+++ b/docs/history/15-o3/code/tests/overflow_data.zc
@@ -0,0 +1 @@
+fn main() { while (1) asm { 1 } return 0; }
diff --git a/docs/history/15-o3/code/tests/overflow_frame.zc b/docs/history/15-o3/code/tests/overflow_frame.zc
new file mode 100644
index 0000000..3897bf4
--- /dev/null
+++ b/docs/history/15-o3/code/tests/overflow_frame.zc
@@ -0,0 +1,2 @@
+fn big(n) { let a[500]; a[0] = n; return big(n + 1) + a[0]; }
+fn main() { return big(0); }
diff --git a/docs/history/15-o3/code/tests/overflow_return.zc b/docs/history/15-o3/code/tests/overflow_return.zc
new file mode 100644
index 0000000..9d5c622
--- /dev/null
+++ b/docs/history/15-o3/code/tests/overflow_return.zc
@@ -0,0 +1,2 @@
+fn deep(n) { return deep(n + 1) + 1; }
+fn main() { return deep(0); }
diff --git a/docs/history/15-o3/code/tests/runerr.sh b/docs/history/15-o3/code/tests/runerr.sh
new file mode 100755
index 0000000..23ea054
--- /dev/null
+++ b/docs/history/15-o3/code/tests/runerr.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+# runs every .zc in a folder (tests/errors by default) and checks the compiler
+# rejects it with the message on its first line: // expect: ...
+vm=${1:-zy}; dir=${2:-$(dirname "$0")/errors}; pass=0; fail=0
+for f in "$dir"/*.zc; do
+ want=$(head -1 "$f" | sed 's#^// expect: ##')
+ got=$($vm < "$f" 2>&1 | head -1)
+ if [[ "$got" == *"$want"* ]]; then pass=$((pass+1)); else fail=$((fail+1)); echo "FAIL $(basename $f): got [$got]"; fi
+done
+echo "rejected: $pass passed, $fail failed"
diff --git a/docs/history/15-o3/code/tests/typeerrors/arg.zc b/docs/history/15-o3/code/tests/typeerrors/arg.zc
new file mode 100644
index 0000000..909672a
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeerrors/arg.zc
@@ -0,0 +1,5 @@
+// expect: type error: argument: expected *point, got *int
+typecheck on;
+struct point { int x, y; };
+int f(*point p) { return p.x; }
+fn main() { *int q = new(int); f(q); free(d*q); return 0; }
diff --git a/docs/history/15-o3/code/tests/typeerrors/arith.zc b/docs/history/15-o3/code/tests/typeerrors/arith.zc
new file mode 100644
index 0000000..76e3111
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeerrors/arith.zc
@@ -0,0 +1,3 @@
+// expect: type error: arithmetic needs numbers, got *int
+typecheck on;
+fn main() { *int p = 0; int x = p * 2; return x; }
diff --git a/docs/history/15-o3/code/tests/typeerrors/assign.zc b/docs/history/15-o3/code/tests/typeerrors/assign.zc
new file mode 100644
index 0000000..8b8a888
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeerrors/assign.zc
@@ -0,0 +1,3 @@
+// expect: type error: initializer: expected *char, got int
+typecheck on;
+fn main() { *char s = 5; return 0; }
diff --git a/docs/history/15-o3/code/tests/typeerrors/call.zc b/docs/history/15-o3/code/tests/typeerrors/call.zc
new file mode 100644
index 0000000..7b03dbf
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeerrors/call.zc
@@ -0,0 +1,3 @@
+// expect: type error: only functions can be called, got int
+typecheck on;
+fn main() { int k = 3; return k(1); }
diff --git a/docs/history/15-o3/code/tests/typeerrors/deref.zc b/docs/history/15-o3/code/tests/typeerrors/deref.zc
new file mode 100644
index 0000000..ed030f5
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeerrors/deref.zc
@@ -0,0 +1,3 @@
+// expect: type error: only pointers can be dereferenced, got int
+typecheck on;
+fn main() { int x = 3; return *x; }
diff --git a/docs/history/15-o3/code/tests/typeerrors/field.zc b/docs/history/15-o3/code/tests/typeerrors/field.zc
new file mode 100644
index 0000000..ba03697
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeerrors/field.zc
@@ -0,0 +1,3 @@
+// expect: point has no field z
+struct point { int x, y; };
+fn main() { point p; return p.z; }
diff --git a/docs/history/15-o3/code/tests/typeerrors/generic.zc b/docs/history/15-o3/code/tests/typeerrors/generic.zc
new file mode 100644
index 0000000..6d4c4da
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeerrors/generic.zc
@@ -0,0 +1,4 @@
+// expect: type error: argument: expected int, got *char
+typecheck on;
+<T> T same(T a, T b) { return a; }
+fn main() { return same(1, "x"); }
diff --git a/docs/history/15-o3/code/tests/typeerrors/notstruct.zc b/docs/history/15-o3/code/tests/typeerrors/notstruct.zc
new file mode 100644
index 0000000..0dff6bd
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeerrors/notstruct.zc
@@ -0,0 +1,2 @@
+// expect: .x: that's a int, not a struct or a pointer to one
+fn main() { int n = 1; return n.x; }
diff --git a/docs/history/15-o3/code/tests/typeerrors/ret.zc b/docs/history/15-o3/code/tests/typeerrors/ret.zc
new file mode 100644
index 0000000..be951d5
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeerrors/ret.zc
@@ -0,0 +1,4 @@
+// expect: type error: return value: expected bool, got *char
+typecheck on;
+bool f() { return "no"; }
+fn main() { return f(); }
diff --git a/docs/history/15-o3/code/tests/typeerrors/voidret.zc b/docs/history/15-o3/code/tests/typeerrors/voidret.zc
new file mode 100644
index 0000000..86c9c17
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeerrors/voidret.zc
@@ -0,0 +1,4 @@
+// expect: type error: return value: expected void, got int
+typecheck on;
+void f() { return 3; }
+fn main() { f(); return 0; }
diff --git a/docs/history/15-o3/code/tests/typeoff.zc b/docs/history/15-o3/code/tests/typeoff.zc
new file mode 100644
index 0000000..e44e450
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeoff.zc
@@ -0,0 +1,2 @@
+// the typechecker is off unless asked for: this mismatch compiles and runs
+fn main() { *char s = 5; putn(s as int + 1); putc(10); return 0; }
diff --git a/docs/history/15-o3/code/tests/typeok.zc b/docs/history/15-o3/code/tests/typeok.zc
new file mode 100644
index 0000000..2d1997b
--- /dev/null
+++ b/docs/history/15-o3/code/tests/typeok.zc
@@ -0,0 +1,21 @@
+// passes the typechecker: words mix with everything, literal 0 is null,
+// arrays decay to pointers, generic calls bind their parameters
+typecheck on;
+struct pair<A, B> { A a; B b; };
+<A, B> *pair<A, B>.u mk(A x, B y) { *pair<A, B> p = new(pair<A, B>); p.a = x; p.b = y; return u*p; }
+<A, B> A left(*pair<A, B> p) { return p.a; }
+int strlen2(*char s) { int n = 0; while (s[n]) n++; return n; }
+int main() {
+ *pair<int, *char> p = mk(3, "four");
+ int a = left(p);
+ *char s = p.b;
+ [8]char buf;
+ *char q = buf;
+ *int nothing = 0;
+ let w = 5;
+ int mixed = w + 1;
+ q[0] = 'h'; q[1] = 0;
+ putn(a + strlen2(s) + strlen2(buf) + mixed); putc(10);
+ free(d*p);
+ return 0;
+}
diff --git a/docs/history/15-o3/code/tests/types.zc b/docs/history/15-o3/code/tests/types.zc
new file mode 100644
index 0000000..3408d10
--- /dev/null
+++ b/docs/history/15-o3/code/tests/types.zc
@@ -0,0 +1,56 @@
+struct point { int x, y; };
+struct rect { point a, b; int tag; };
+struct list<T> { int len; *T items; };
+struct node { int v; *node next; };
+type num = int;
+
+int counter = 5;
+[4]int squares;
+point origin;
+
+int area(*rect r) { return (r.b.x - r.a.x) * (r.b.y - r.a.y); }
+<T> *list<T>.u mklist(int cap) {
+ *list<T> l = new(list<T>);
+ l.len = 0;
+ l.items = new(T, cap) as *T;
+ return u*l;
+}
+<T> T first(*list<T> l) { return l.items[0]; }
+int sum(*node n) { int s = 0; while (n) { s += n.v; n = n.next; } return s; }
+
+fn main()
+{
+ rect r;
+ r.a.x = 1; r.a.y = 2; r.b.x = 4; r.b.y = 6;
+ putn(area(&r)); putc(' ');
+ putn(sizeof(rect)); putc(' '); putn(sizeof(point)); putc(' '); putn(sizeof([3]rect)); putc(' ');
+ let p = new(point); // let infers *point
+ p.x = 7; p->y = 8;
+ putn(p.x * p.y); putc(' ');
+ free(d*p);
+ *list<int> l = mklist<int>(4);
+ l.items[0] = 42; l.len = 1;
+ putn(first(l)); putc(' ');
+ free(d*l);
+ *node a = new(node), b = new(node);
+ a.v = 3; b.v = 4; a.next = b;
+ putn(sum(a)); putc(' ');
+ free(d*a); // b went with it: a.next = b made a its owner
+ [3]point pts;
+ pts[2].y = 9;
+ putn(pts[2].y + counter); putc(' ');
+ num k = 3;
+ putn(k); putc(10);
+
+ // reflection
+ tprint(typeof(r)); putc(' '); tprint(typeof(&r)); putc(' '); tprint(typeof(l)); putc(' ');
+ tprint(sigof(mklist)); putc(' '); tprint(sigof(first)); putc(' '); tprint(sigofs("free")); putc(10);
+ let t = typeof(r), i = 0;
+ while (i < tnfields(t)) { puts(tfname(t, i)); putc(':'); tprint(tftype(t, i)); putc('@'); putn(tfoff(t, i)); putc(' '); i++; }
+ putc(10);
+ *point q = new(point);
+ tprint(typeof_obj(q)); putc(' '); putn(typeof_obj(q) == typeof(point)); putc(' '); putn(tkind(typeof_obj(q)) == TY_STRUCT);
+ free(d*q);
+ putc(10);
+ return 0;
+}
diff --git a/docs/history/15-o3/code/tests/umd.zc b/docs/history/15-o3/code/tests/umd.zc
new file mode 100644
index 0000000..497c9fc
--- /dev/null
+++ b/docs/history/15-o3/code/tests/umd.zc
@@ -0,0 +1,51 @@
+// lists as [len, items...], items owned by the list
+.u mklist(n) {
+ let l = new(n + 1);
+ l[0] = 0;
+ return u*l;
+}
+fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; }
+fn sum(.m l) { let s = 0, i = 1; while (i <= l[0]) { s += l[i][0]; i++; } return s; }
+.u box(v) { let b = new(1); b[0] = v; return u*b; }
+fn consume(.d b) { return b[0]; }
+
+fn main()
+{
+ let base = live_objects();
+ {
+ let l = mklist(8); // u from mklist arrives m*here
+ let i = 0;
+ while (i < 5) {
+ let b = box(i * 10); // m in the loop body
+ d*b; // give it down
+ push(l, b);
+ i++;
+ }
+ putn(sum(l)); putc(' ');
+ putn(live_objects() - base); putc(' '); // list + 5 boxes
+ }
+ putn(live_objects() - base); putc(' '); // freed at the block's end, children too
+
+ // u handed up one block by assignment, and as a block value
+ let outer;
+ {
+ let t = box(7);
+ u*t;
+ outer = t;
+ }
+ let v = { let w = box(8); u*w };
+ putn(outer[0] + v[0]); putc(' ');
+
+ // d given away to a callee
+ let g = box(99);
+ putn(consume(d*g)); putc(' ');
+
+ // containers: child takes its parent's tag
+ let p = new(2), c = new(1);
+ c[0] = 5;
+ p[0] = c;
+ p[1] = new(3);
+ putn(p[0][0]); putc(' ');
+ putn(live_objects() - base); putc(10);
+ return 0;
+}