Problem
Compiler-minted local names leak pipeline history into the emitted Lua text. Fresh names are drawn as x$N (rendered x_S_N by the Lua name mangler) from one monotone supply threaded through the whole IR pipeline — the uniquify pass, freshening on inline pastes, CSE, uncurrying all consume it — so any change that shifts the count of names consumed early in a module renames every later binder by a constant offset, even in code the change never touched. PR #304 showed the cost at review time: of the 26 golden modules that moved, several changed in nothing but indices (e.g. Golden.NativeLoopsGuard's v_S_1437 → v_S_1440 — the modules merely import unsafe-coerce transitively, and the newly lifted λx. x binder consumes supply indices where the binder-free accessor consumed none). This alpha-only churn is pure noise in golden diffs, inflates every optimizer PR's review surface, and forces PSLUA_GOLDEN_ACCEPT runs for changes that are semantically identity.
Approach
Normalize at emission, not in the IR: the supply must stay global — the pipeline's unique-binders invariant (GUC) depends on it — so quarantine the fix in a late Chunk → Chunk pass that runs after every name-minting Lua-level transform (localization/promotion, loopification) and immediately before the printer. The pass walks the chunk and renumbers every $N-suffixed local binder in first-occurrence order, rewriting its references; unsuffixed names are untouched. The domain is well-defined because PureScript identifiers cannot contain $, so a $-suffix marks a compiler-minted name exclusively; sequential renumbering preserves uniqueness, so the pre-existing no-capture property carries over unchanged. Emitted names thereby become a function of the artifact's own structure — byte-stable under any upstream change that only perturbs supply consumption. Nothing semantic reads these names: FFI tables key exports by export name, the runtime-lazy factory is keyed by a string literal, and the locals/upvalue budget counting is name-agnostic. Landing the pass causes a one-time whole-corpus golden renumbering, accepted once; a possible follow-up (out of scope here) is dropping the suffix entirely where the base name is unambiguous in scope, which needs real shadowing analysis rather than renumbering.
Prerequisites / Relations
Independent. Sibling of the emission-ergonomics issues #276 (no width bound on emitted lines) and #273 (lua-language-server diagnostics on call sites); reduces the golden-churn class that #303 discusses from the harness side.
Verification / Measurement
The red is demonstrable as a differential: burn a synthetic offset of supply indices at pipeline start (a test-only knob or a prepended dummy binder) and recompile the golden corpus — today every module's golden.lua churns in indices; with the pass, all artifacts are byte-identical under the perturbation. Plus: the pass is idempotent (normalizing twice equals once), luacheck stays clean, and every hand-verified eval oracle is unchanged — renumbering is alpha-renaming by construction, and the eval goldens prove it on the corpus.
Problem
Compiler-minted local names leak pipeline history into the emitted Lua text. Fresh names are drawn as
x$N(renderedx_S_Nby the Lua name mangler) from one monotone supply threaded through the whole IR pipeline — the uniquify pass, freshening on inline pastes, CSE, uncurrying all consume it — so any change that shifts the count of names consumed early in a module renames every later binder by a constant offset, even in code the change never touched. PR #304 showed the cost at review time: of the 26 golden modules that moved, several changed in nothing but indices (e.g.Golden.NativeLoopsGuard'sv_S_1437 → v_S_1440— the modules merely importunsafe-coercetransitively, and the newly liftedλx. xbinder consumes supply indices where the binder-free accessor consumed none). This alpha-only churn is pure noise in golden diffs, inflates every optimizer PR's review surface, and forcesPSLUA_GOLDEN_ACCEPTruns for changes that are semantically identity.Approach
Normalize at emission, not in the IR: the supply must stay global — the pipeline's unique-binders invariant (GUC) depends on it — so quarantine the fix in a late
Chunk → Chunkpass that runs after every name-minting Lua-level transform (localization/promotion, loopification) and immediately before the printer. The pass walks the chunk and renumbers every$N-suffixed local binder in first-occurrence order, rewriting its references; unsuffixed names are untouched. The domain is well-defined because PureScript identifiers cannot contain$, so a$-suffix marks a compiler-minted name exclusively; sequential renumbering preserves uniqueness, so the pre-existing no-capture property carries over unchanged. Emitted names thereby become a function of the artifact's own structure — byte-stable under any upstream change that only perturbs supply consumption. Nothing semantic reads these names: FFI tables key exports by export name, the runtime-lazy factory is keyed by a string literal, and the locals/upvalue budget counting is name-agnostic. Landing the pass causes a one-time whole-corpus golden renumbering, accepted once; a possible follow-up (out of scope here) is dropping the suffix entirely where the base name is unambiguous in scope, which needs real shadowing analysis rather than renumbering.Prerequisites / Relations
Independent. Sibling of the emission-ergonomics issues #276 (no width bound on emitted lines) and #273 (lua-language-server diagnostics on call sites); reduces the golden-churn class that #303 discusses from the harness side.
Verification / Measurement
The red is demonstrable as a differential: burn a synthetic offset of supply indices at pipeline start (a test-only knob or a prepended dummy binder) and recompile the golden corpus — today every module's
golden.luachurns in indices; with the pass, all artifacts are byte-identical under the perturbation. Plus: the pass is idempotent (normalizing twice equals once),luacheckstays clean, and every hand-verified eval oracle is unchanged — renumbering is alpha-renaming by construction, and the eval goldens prove it on the corpus.