plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


28a3f9f — Peter John 2026-11-04T19:43:35+05:30
docs: compiler cleanup phase 3a plan
docs/plans/2026-11-04-compiler-cleanup-phase3a.md ADDED
@@ -0,0 +1,112 @@
1
+ # Compiler cleanup, phase 3a: per-compile state, no fusion globals
2
+
3
+ **Review plan:** docs/plans/2026-11-04-compiler-cleanup.review.md
4
+ **Goal:** The checker's ~23 thread-locals and codegen's 6 become one state value each, created fresh per compile; plum-core's process-global counters restart per compile; the fusion-options thread-local and the CLI's fusion OnceLock are gone. No behaviour change. (Phase 3b: pass-list driver and positioned errors.)
5
+ **Architecture:** Rust workspace (plum-core, plum-checker, plum-wasm-codegen, plum-cli, plum-lsp, plum-host, plum-test-support); plum-game separate.
6
+ **Tech stack:** Rust 2021, cargo, wasmtime 47.
7
+ **Detail:** contracts
8
+ **Target-min:** 20
9
+
10
+ ## Global constraints
11
+ - Agents cannot run `cargo test` (hook): `cargo build -p <crate> --tests`, run binaries in `target/debug/deps` (newest by mtime). `touch` edited files first. Use your own scratchpad subdirectory and CARGO_TARGET_DIR if a build is locked.
12
+ - No behaviour change: all 25 examples sha256-identical vs the start sha (`.superlite/progress.md`) via `bash scripts/wasm-hashes.sh <sha> OUT`; `plum test plum-std plum-examples` 678 passed.
13
+ - This is a middle step, not full method-ization: state moves into one struct per crate behind one scoped accessor (`with…State(|s| …)`), reset at the compile/check entry. Don't rewrite function signatures across the file.
14
+ - camelCase fns. Never run `plum format` on real files. Don't commit.
15
+
16
+ ## Main diagram
17
+ ```mermaid
18
+ flowchart LR
19
+ E["compile / typedSourceWith entry"] --> R["reset: CheckState, GenState, core counters"]
20
+ R --> C["checker reads CheckState"]
21
+ R --> G["codegen reads GenState"]
22
+ O["CompileOptions.fusion"] --> C
23
+ ```
24
+
25
+ ## Task graph
26
+ ```mermaid
27
+ flowchart LR
28
+ T1[checker state]
29
+ T2[codegen state]
30
+ T3[core counters]
31
+ T4[fusion globals out]
32
+ T5[verify 3a]
33
+ %% taskgraph
34
+ T1 --> T4
35
+ T2 --> T4
36
+ T1 --> T5
37
+ T2 --> T5
38
+ T3 --> T5
39
+ T4 --> T5
40
+ ```
41
+ Ruling: sequential - T4 edits checker and codegen lib.rs after T1/T2 - cost: one extra step.
42
+
43
+ ## Package A: state
44
+ **Probe entry:** run=bash scripts/wasm-hashes.sh
45
+
46
+ ### T1 checker state value · Complexity 5 · Est. context 55k
47
+ **Owns:** plum-checker/src/lib.rs, plum-checker/src/unions.rs, plum-checker/src/traits.rs, plum-checker/src/state.rs
48
+ **Components:** 1
49
+ **LOC owned:** 140
50
+ **Tests:** slow
51
+ **Model:** sonnet
52
+ **Produces:** state::CheckState (all former thread-local fields) · state::withCheck<R>(f: impl FnOnce(&mut CheckState) -> R) -> R · state::resetCheck()
53
+ e.g. resetCheck(); withCheck(|s| s.fresh) -> 0
54
+ e.g. two typedSource calls on the same source -> identical TypedInfo and identical generated names
55
+ **Review Focus:** `reset_complete`: every field the old `checkSourceOnce` reset by hand is reset by `resetCheck`, and every save/restore pair (union plan in adoptPre/recheckKeep) keeps its semantics; `reentrancy`: no `withCheck` borrow is held across a call that re-enters `withCheck` (use short borrows / Cell-like fields; a RefCell double borrow panics — run the full checker suite to prove none).
56
+ **Test code:** a `#[cfg(test)]` test for the two e.g. lines; all checker, plum-core and codegen test binaries pass; hashes identical.
57
+ **Steps:** move lib.rs 17-58 thread-locals, ALLOW_BARE_TOP, unions.rs IN_WRAP/DEPTH/INLINE/NARROW_TOP and traits.rs TRAITS/TRAIT_FIELDS into `CheckState`; keep FUSION_OPTIONS for T4; COVER_WILDCARD is a constant, leave it.
58
+
59
+ ### T2 codegen state value · Complexity 4 · Est. context 50k
60
+ **Owns:** plum-wasm-codegen/src/lib.rs
61
+ **Components:** 1
62
+ **LOC owned:** 80
63
+ **Tests:** slow
64
+ **Model:** sonnet
65
+ **Produces:** GenState { gc_types, consts, value_enums, value_block_types, value_helpers, err_info } · withGen<R>(f: impl FnOnce(&mut GenState) -> R) -> R
66
+ e.g. compile twice in one process -> identical bytes
67
+ **Review Focus:** `reentrancy` as in T1; `withGcTypes` indirection folded into `withGen`.
68
+ **Test code:** a `#[cfg(test)]` test compiling one source twice in-process → equal bytes; codegen test binaries pass; hashes identical.
69
+ **Steps:** CURRENT_GC_TYPES, CURRENT_CONSTS, VALUE_ENUMS, VALUE_BLOCK_TYPES, VALUE_HELPERS, ERR_INFO (lib.rs ≈562-776) → one GenState, reset at the start of `compile`.
70
+
71
+ ### T3 core counters per compile · Complexity 3 · Est. context 40k
72
+ **Owns:** plum-core/src/forms.rs, plum-core/src/simplify.rs, plum-core/src/unions.rs, plum-core/src/loader.rs, plum-core/src/lower.rs, plum-core/src/session.rs, plum-core/src/lib.rs, plum-core/tests/session_test.rs
73
+ **Components:** 1
74
+ **LOC owned:** 70
75
+ **Tests:** fast
76
+ **Model:** sonnet
77
+ **Produces:** session::begin() (resets forms NEXT_LOOP_ID/COUNTER/REG/LOWERED/ERRORS/USES/SEQ_KINDS, simplify FRESH, unions PLAN/FRESH, loader USER_PRIMS/MERGED_LIT_ERRORS)
78
+ e.g. begin(); load+lower a program twice in one process -> identical ASTs (same generated names)
79
+ **Review Focus:** `begin_called_once`: `lower::frontEnd` calls `session::begin()` first; nothing resets mid-compile (the forms session begin/active/finish protocol keeps working).
80
+ **Test code:** session_test.rs for the e.g. line; plum-core test binaries pass; hashes identical.
81
+
82
+ ### T4 fusion globals out · Complexity 3 · Est. context 40k
83
+ **Owns:** plum-checker/src/lib.rs, plum-wasm-codegen/src/lib.rs, plum-cli/src/main.rs, plum-cli/src/game.rs, plum-core/tests/trmc_test.rs, plum-wasm-codegen/tests/fusion_alloc_test.rs, plum-wasm-codegen/tests/e2e_mem/reuse.rs
84
+ **Components:** 2
85
+ **LOC owned:** 70
86
+ **Tests:** slow
87
+ **Model:** sonnet
88
+ **Consumes:** state::withCheck<R>(f: impl FnOnce(&mut CheckState) -> R) -> R (T1) · withGen<R>(f: impl FnOnce(&mut GenState) -> R) -> R (T2)
89
+ **Produces:** none
90
+ **Review Focus:** `cli_flags`: `--no-fusion` / `--explain-fusion` output identical to the start sha's binary (also via `plum game`); `no_globals`: `FUSION_OPTIONS`, `setFusionOptions`, `withFusionOptions`, `fusionOptions` and the CLI `static FUSION` are deleted.
91
+ **Test code:** cli test binaries, trmc_test, fusion_alloc_test, e2e_mem pass; hashes identical.
92
+ **Steps:** tests pass options via `compile(&src, &CompileOptions{fusion, ..})` / `typedSourceWith`; `compileSource`/`typedSource` use `FusionOptions::default()`; the CLI threads options from argv into `compileToWasm(file, lib, opts)` (game.rs too).
93
+
94
+ ## Package B: verify
95
+ **Probe entry:** run=bash scripts/wasm-hashes.sh
96
+
97
+ ### T5 verify 3a · Complexity 2 · Est. context 30k
98
+ **Owns:** docs/plans/2026-11-04-compiler-cleanup-phase3a.results.md
99
+ **Components:** 1
100
+ **LOC owned:** 40
101
+ **Tests:** slow
102
+ **Model:** sonnet
103
+ **Produces:** none
104
+ **Test code:** all workspace test binaries, plum-game host_test, plum test 678, hashes identical to start sha; `grep -rn "thread_local!" plum-*/src` lists only the new state holders plus forms' session (report each).
105
+ **Steps:** results file: thread-local count before/after per crate, hash diff, test totals.
106
+
107
+ ## Review Focus
108
+ - Package A: state, deep — RefCell re-entrancy panics, incomplete resets, save/restore semantics.
109
+ - Package B: file.
110
+
111
+ ## Self-review
112
+ Covers the phase 3 state row (Checker/Codegen state, per-compile counters) and two 2b leftovers (fusion thread-local, CLI OnceLock). Pass list and positioned errors are phase 3b.