plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
58b6f33
— Peter John
2026-11-03T20:51:15+05:30
docs(plans): supercompiler: uniqueness inference stage to test whether own is needed
docs/plans/2026-11-03-supercompiler.review.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Supercompiler: fusing List chains and view-into-draw: review plan
|
|
2
2
|
|
|
3
|
-
**Builds on:**
|
|
3
|
+
**Builds on:** master after `2026-11-03-pure-scene-enums` (pure scene on `Node` + `Kind`, `own` kept): closure specialisation, tail calls and TRMC, unboxed variants, std cons `List`, recursive `Picture` and the state/view Orb Run.
|
|
4
|
+
|
|
5
|
+
**Added 2026-11-03 (user):** a uniqueness stage. `own` stays for now. If the compiler can prove for itself that nobody else still holds a value, `own` becomes redundant and can be removed in a later plan. See "Uniqueness without `own`" below.
|
|
4
6
|
|
|
5
7
|
## Summary
|
|
6
8
|
Two kinds of code still build data only to take it apart straight away:
|
|
@@ -16,6 +18,16 @@ A small **supercompiler** removes both. It is a compiler pass that, when one fun
|
|
|
16
18
|
- **Pure code only.** The pass fuses only when every function and closure involved is pure (no `extern` calls, no field or variable writes outside its own locals, no `print`). Fusing changes the order in which steps run (`f`, `g` and `h` interleave per element instead of running stage by stage), and pure code can't tell the difference. Anything else is left exactly as it is today.
|
|
17
19
|
- **Visible results.** A `plum build --explain-fusion` flag (or an env var, whichever fits the CLI better) lists each fused site and each place where the pass gave up, and why.
|
|
18
20
|
|
|
21
|
+
## Uniqueness without `own`
|
|
22
|
+
Today `own` is how a function such as `update(own n) -> Node` changes `n` in place and hands it back: the checker proves no one else still holds `n`. Koka and Lean get the same in-place updates with no keyword, by checking at runtime that a value has one reference. Plum has no reference counts (wasm-gc), so the proof has to be static. Whole-program compilation makes it tractable.
|
|
23
|
+
|
|
24
|
+
- **Inference.** For every spread copy (`Node(..n, x: 1.0)`) and every rebuilt constructor, the pass checks whether the source value is dead afterwards on every path and is not reachable from anything still live. Live things include another binding, a field of a live value, an element of a live collection, a closure capture or a global. If so, the copy becomes an in-place write, as `own` does today.
|
|
25
|
+
- **Reuse.** A constructor that takes a matched value apart and builds one of the same shape (e.g. `Cons(h, t) => Cons(f(h), go(t))`) reuses the old cell's memory, Koka's "reuse" idea.
|
|
26
|
+
- **Measured against `own`.** The pass runs on the code with every `own` erased, and the plan reports how many of today's `own` in-place sites it proves on its own across std, `game_pure_scene` and its fixture.
|
|
27
|
+
- If it proves all of them, a follow-up plan removes the `own` keyword and the ownership checker.
|
|
28
|
+
- If not, the report lists the sites it can't prove and why, and `own` stays.
|
|
29
|
+
- **Never unsound.** Anything unclear stays a copy. A copy is always correct, only slower.
|
|
30
|
+
|
|
19
31
|
## What will be built
|
|
20
32
|
| Part | What it does | Size |
|
|
21
33
|
|---|---|---|
|
|
@@ -25,6 +37,7 @@ A small **supercompiler** removes both. It is a compiler pass that, when one fun
|
|
|
25
37
|
| Stop rule | A growth check (homeomorphic embedding plus a size budget per site) stops driving that would never end or would blow up code size; the site is left unfused. No generalisation in this round | M |
|
|
26
38
|
| Std reshaping | `List.filter` written in direct recursive style (TRMC still makes it constant-stack) so it fuses; `Picture`'s draw, bounds and hit walks checked to be pure; `toNodes`-style helpers in the example kept fusable | S |
|
|
27
39
|
| Pipeline placement | Runs after closure specialisation (so closures are already inlined) and before TRMC and tail-call marking (so the fused functions still get loops and `return_call`) | S |
|
|
40
|
+
| Uniqueness inference | Dead-after-use analysis turns spread copies and same-shape rebuilds into in-place writes or cell reuse; a report against today's `own` sites with `own` erased | L |
|
|
28
41
|
| Benchmarks and docs | 1M List pipeline table, state/view vs mutable vs pure scene table, README "how fusion works" section | S |
|
|
29
42
|
|
|
30
43
|
## How it fits together
|