Summary
The per-module object cache at .perry-cache/objects/ is keyed by hash-of-source only. When HIR-level transform passes (e.g. crates/perry-transform/src/state_desugar.rs) change but the user's .ts file doesn't, the cache returns a .o produced by the old pass — even though the new pass would emit different LLVM IR for the same source.
This silently masks correctness regressions during local development of new HIR passes.
How to reproduce
- Add a new HIR transform pass that rewrites some shape into runtime FFI calls.
- Build & run a
.ts file that exercises the pass — it works (cache miss, fresh codegen).
- Edit the pass (introduce a regression, or a new branch). Rebuild perry. Don't touch the
.ts.
- Re-run on the same
.ts — the cached .o is reused, the pass change is invisible at runtime.
What I hit
While shipping #535 Layer 1 (commit be071793), I spent ~45 minutes debugging a runtime TypeError on count.set(count.get() + 1) that was fully resolved at the HIR level (verified via --print-hir). The runtime kept calling js_state_init correctly because the cached .o from an earlier successful build had the right calls baked in — but the new debug code paths I added to the codegen never executed. Confirmed by strings ./perry | grep CODEGEN-DBG showing the eprintln strings present in the perry binary, but no output at runtime. rm -rf /tmp/.perry-cache && rebuild immediately exposed the actual bug (a mismatched pre-Pass-update cached object).
Suggested fix
Two options, in order of effort:
- Quick: include perry's own version (
workspace.package.version) or a build-id of libperry_codegen.a in the cache key. Any cargo build -p perry-codegen invalidates everything, which is what we want during pass development.
- Better: include hashes of the perry-codegen / perry-transform / perry-hir crate
.rlibs in the cache key. Less aggressive — only HIR/codegen/transform changes invalidate.
The status-quo (hash-of-source-only) is fine for cargo run against an unchanged perry binary, but pretty hostile to anyone working on a new HIR pass.
Discovery context
Found while implementing #535 Layer 1. Cost: ~45 min of debugging a phantom bug. Filing as a separate issue so the workaround (rm -rf <project>/.perry-cache) is documented and the cache-key invalidation gets a real fix.
Summary
The per-module object cache at
.perry-cache/objects/is keyed by hash-of-source only. When HIR-level transform passes (e.g.crates/perry-transform/src/state_desugar.rs) change but the user's.tsfile doesn't, the cache returns a.oproduced by the old pass — even though the new pass would emit different LLVM IR for the same source.This silently masks correctness regressions during local development of new HIR passes.
How to reproduce
.tsfile that exercises the pass — it works (cache miss, fresh codegen)..ts..ts— the cached.ois reused, the pass change is invisible at runtime.What I hit
While shipping #535 Layer 1 (commit
be071793), I spent ~45 minutes debugging a runtime TypeError oncount.set(count.get() + 1)that was fully resolved at the HIR level (verified via--print-hir). The runtime kept callingjs_state_initcorrectly because the cached.ofrom an earlier successful build had the right calls baked in — but the new debug code paths I added to the codegen never executed. Confirmed bystrings ./perry | grep CODEGEN-DBGshowing the eprintln strings present in the perry binary, but no output at runtime.rm -rf /tmp/.perry-cache && rebuildimmediately exposed the actual bug (a mismatched pre-Pass-update cached object).Suggested fix
Two options, in order of effort:
workspace.package.version) or a build-id oflibperry_codegen.ain the cache key. Anycargo build -p perry-codegeninvalidates everything, which is what we want during pass development..rlibs in the cache key. Less aggressive — only HIR/codegen/transform changes invalidate.The status-quo (hash-of-source-only) is fine for
cargo runagainst an unchanged perry binary, but pretty hostile to anyone working on a new HIR pass.Discovery context
Found while implementing #535 Layer 1. Cost: ~45 min of debugging a phantom bug. Filing as a separate issue so the workaround (
rm -rf <project>/.perry-cache) is documented and the cache-key invalidation gets a real fix.