Independent reproducer
This is a synchronous language-semantics bug, not an application/Bun API dependency.
The source below runs without a bundle, credentials, network, or the generator fix.
function test() {
const results: string[] = [];
for (let index = 0; index < 2; index++) {
let read = () => value;
try { read(); results.push('missing-tdz'); }
catch (error) { results.push(error instanceof ReferenceError ? 'tdz' : 'wrong-error'); }
let value: number;
results.push(String(read()));
value = index;
results.push(String(read()));
let recurse = (n: number): number => n === 0 ? value : recurse(n - 1);
results.push(String(recurse(2)));
}
return results.join(',');
}
console.log(test());
Pinned Node 26.5.1 prints:
tdz,undefined,0,0,tdz,undefined,1,1
Perry prints:
tdz,undefined,0,0,missing-tdz,undefined,1,1
The second iteration's read before the let declaration must throw ReferenceError.
The no-initializer declaration must then end that iteration's TDZ with undefined.
Before/after evidence
macOS arm64, Wasm host enabled, compact GC (RS4GC=0, shadow stack=1,
inline shadow slot=0, full outline IC=1), auto-optimization disabled.
Every case uses a frozen compiler and all nine coherent provider archives from
its exact source commit, not mixed libraries or source-stamp overrides.
Identical failure at both O0 and Oz on all three trees:
Thus this is not introduced by #10049. Its initial broad control fixture exposed
the existing bug. The separate generator callback reproduction now passes.
Example native compile (with a matched build's runtime directory configured):
PERRY_LL_OPT_LEVEL=0 perry compile tdz-loop.ts -o tdz-loop --platform bun --enable-wasm-runtime --no-auto-optimize
Use fresh output/cache paths and run under a 15-second timeout.
Implementation scope / acceptance
- Preserve per-iteration lexical binding identity and reestablish TDZ on every
block entry; do not reset shared var bindings or reuse a previous iteration's cell.
- Audit TDZ box preallocation and loop capture/freshening paths. The precise
responsible lowering path has not yet been isolated; this issue is behavioral evidence.
- Add a byte-for-byte Node/native gap regression, then cover retained callbacks,
initialized and uninitialized let, const, recursion, and shared var controls.
- Verify O0/Os/Oz under default and compact GC. No application rebuild is required.
Local evidence: tdz-loop-before-after-20260911.log and the six-case result
record /var/folders/8v/09vl257j2_v70v2k77gh01n00000gn/T/perry-tdz-loop-ab-szGh6Q/result.json.
Independent reproducer
This is a synchronous language-semantics bug, not an application/Bun API dependency.
The source below runs without a bundle, credentials, network, or the generator fix.
Pinned Node 26.5.1 prints:
tdz,undefined,0,0,tdz,undefined,1,1Perry prints:
tdz,undefined,0,0,missing-tdz,undefined,1,1The second iteration's read before the
letdeclaration must throw ReferenceError.The no-initializer declaration must then end that iteration's TDZ with undefined.
Before/after evidence
macOS arm64, Wasm host enabled, compact GC (RS4GC=0, shadow stack=1,
inline shadow slot=0, full outline IC=1), auto-optimization disabled.
Every case uses a frozen compiler and all nine coherent provider archives from
its exact source commit, not mixed libraries or source-stamp overrides.
Identical failure at both O0 and Oz on all three trees:
53df2c671fff33b1f8f624432372c2401db8b1d0(0.5.1530);f3e8d5d1a66753cb00d15da552ad73abb8740520, main603b074acplus unrelated fix(child_process): finish output readers after EOF and spawn failure #10042/fix(runtime): honor AsyncResource.bind and other own function overrides #10046 compatibility fixes;
81b47b47cace5b150edde32059ff272a3a19eca5, main603b074acplus the missing ordinary-boxed-declaration-cell fix in fix(codegen): initialize missing cells in reused boxed declarations #10049.
Thus this is not introduced by #10049. Its initial broad control fixture exposed
the existing bug. The separate generator callback reproduction now passes.
Example native compile (with a matched build's runtime directory configured):
PERRY_LL_OPT_LEVEL=0 perry compile tdz-loop.ts -o tdz-loop --platform bun --enable-wasm-runtime --no-auto-optimizeUse fresh output/cache paths and run under a 15-second timeout.
Implementation scope / acceptance
block entry; do not reset shared
varbindings or reuse a previous iteration's cell.responsible lowering path has not yet been isolated; this issue is behavioral evidence.
initialized and uninitialized
let,const, recursion, and sharedvarcontrols.Local evidence:
tdz-loop-before-after-20260911.logand the six-case resultrecord
/var/folders/8v/09vl257j2_v70v2k77gh01n00000gn/T/perry-tdz-loop-ab-szGh6Q/result.json.