Avoid cloning cached declaration types after truncation - #63969
Avoid cloning cached declaration types after truncation#63969Butros J. G. Groot (butros10games) wants to merge 3 commits into
Conversation
|
@microsoft-github-policy-service agree |
There was a problem hiding this comment.
Pull request overview
Optimizes declaration emit for oversized inferred types by eliding cached subtrees after truncation, preventing excessive cloning and memory use.
Changes:
- Adds an internal node-builder truncation flag for declaration emit.
- Replaces oversized cached subtrees with the existing elision placeholder.
- Adds a compiler regression test and diagnostic baseline.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
tsc/internal/checker/nodebuilderimpl.go |
Elides cached types after the serialization limit. |
tsc/internal/nodebuilder/types.go |
Defines the new internal builder flag. |
tsc/internal/transformers/declarations/transform.go |
Enables the optimization for declaration emit. |
tsc/testdata/tests/cases/compiler/declarationEmitOversizedCachedType.ts |
Adds the oversized shared-type reproduction. |
tsc/testdata/baselines/reference/compiler/declarationEmitOversizedCachedType.errors.txt |
Records the expected TS7056 diagnostic. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
| b.ctx.truncating = true | ||
| } | ||
| b.ctx.approximateLength += cachedResult.addedLength | ||
| if b.ctx.internalFlags&nodebuilder.InternalFlagsStopBuildingAfterTruncation != 0 && b.checkTruncationLength() { |
There was a problem hiding this comment.
I don't even think this flag is required. If we're truncating, we're truncating, no?
There was a problem hiding this comment.
Aye, I agree - if we're truncating we emit ... instead of doing more work - this should just be a new place we check-trunating-and-emit ....
There was a problem hiding this comment.
I’ve now committed the removal of the flag.
I initially had it without the flag locally, but started overthinking it. I also updated the two existing type baselines where cached subtrees are now elided after truncation.
Thanks for the review!
84fb6ba to
8477fb3
Compare
There was a problem hiding this comment.
Actually, hold up - I think we wanna move the truncation check up the function - so move the
if b.checkTruncationLength() {
return b.createElidedInformationPlaceholder()
}up to the start of the function. Reason being that if we load a cached node that has cached truncation, we want to continue truncating subsequent vsitAndTransformType calls, not blindly replace the result that already has truncation set (since it should already have inner truncation).
Good catch, I’ve moved the truncation check to the start of |
Fixes #63966
Context
When declaration emit serializes an inferred type, cached type nodes are
deep-cloned when they are reused. The cache avoids recomputing each type, but
every cache hit still materializes a fresh copy of the cached AST subtree.
For deeply shared structural types, those copies can grow exponentially even
after declaration serialization has exceeded its hard limit and will report
TS7056.
This PR
Type serialization now checks the existing node-builder truncation state before
visiting a type. Once the length limit has been reached, it uses the existing
elided-information placeholder instead of building or cloning another subtree.
Cache hits reached before truncation still replay tracked symbols and add their
cached length. If a cached node activates truncation, that node is cloned with
its existing inner elision; subsequent type visits return the placeholder
immediately.
The check lives in the shared type visitor rather than a declaration-specific
mode, matching existing node-builder truncation semantics. Existing compiler
and quick-info expectations are updated where the earlier check changes the
location or shape of the terminal placeholder.
A compiler test covers the issue reproduction without emitting JavaScript or
generating unnecessary type and symbol baselines.
Performance
Measured on the issue reproduction using native
tsc --declaration:mainBoth runs report TS7056, emit identical JavaScript, and write no declaration
file.
Validation
npx hereby test:allnpx hereby lintnpx hereby check:formatThis PR was developed with assistance from OpenAI Codex. I reviewed the final
diff and validation results.