Skip to content

fix: memory-leak due to unbound type-parameter recursion - #112

Merged
pfitzseb merged 2 commits into
julia-vscode:mainfrom
PatrickHaecker:sp/bound-type-parameter-expansion
Jul 6, 2026
Merged

fix: memory-leak due to unbound type-parameter recursion#112
pfitzseb merged 2 commits into
julia-vscode:mainfrom
PatrickHaecker:sp/bound-type-parameter-expansion

Conversation

@PatrickHaecker

Copy link
Copy Markdown

FakeTypeName expanded a type's parameters recursively with no size bound. A few types explode into enormous trees: type-domain packages whose types recurse into themselves (e.g. TypeDomainNaturalNumbers — naturals as nested types, rationals as continued fractions), and even some Base/LinearAlgebra signatures whose where-bounded Unions reach tens of thousands of nodes. This produced multi-gigabyte cache files (2.2 GB for TypeDomainNaturalNumbers) that exhausted memory — OOMing the machine — when read back into the store.

Cap expansion with a per-type budget of expanded DataTypes (MAX_EXPANDED_TYPES = 128): once it is spent, a type's remaining parameters are dropped and only its name is kept. The budget bounds any shape of explosion (depth, width, or indirect recursion through variable bounds), while ordinary types stay far below it and are unaffected.

Ported from julia-vscode/SymbolServer.jl#320 (SymbolServer.jl is deprecated; the code now lives here under shared/symbolserver/).

FakeTypeName expanded a type's parameters recursively with no size bound.
A few types explode into enormous trees: type-domain packages whose types
recurse into themselves (e.g. TypeDomainNaturalNumbers — naturals as nested
types, rationals as continued fractions), and even some Base/LinearAlgebra
signatures whose `where`-bounded `Union`s reach tens of thousands of nodes.
This produced multi-gigabyte cache files (2.2 GB for TypeDomainNaturalNumbers)
that exhausted memory — OOMing the machine — when read back into the store.

Cap expansion with a per-type budget of expanded DataTypes
(MAX_EXPANDED_TYPES = 128): once it is spent, a type's remaining parameters
are dropped and only its name is kept. The budget bounds any shape of
explosion (depth, width, or indirect recursion through variable bounds),
while ordinary types stay far below it and are unaffected. Add a regression
test.

Ported from julia-vscode/SymbolServer.jl#320 (SymbolServer.jl is deprecated;
the code now lives here under shared/symbolserver/).
@codecov

codecov Bot commented Jul 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (48d4f62) to head (b7a7141).
⚠️ Report is 475 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##            main    #112     +/-   ##
=======================================
  Coverage   0.00%   0.00%             
=======================================
  Files         15      56     +41     
  Lines        682    7382   +6700     
=======================================
- Misses       682    7382   +6700     
Flag Coverage Δ
unittests 0.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@pfitzseb

pfitzseb commented Jul 5, 2026

Copy link
Copy Markdown
Member

Is this still required with #108? If yes, would be nice to also emit a Unserializable node here (though that might require widening of some type signatures).

Re TypeDomainNaturalNumbers: IIRC that produced only ~220 mb cache files for me when I last ran the indexer, but I can double check.

…ntinel

Instead of silently dropping all parameters when the ExpandBudget is
spent, keep the type's name and a single Unserializable() parameter, so
truncated types render as Foo{…} instead of a misleadingly bare Foo.
Budget is now only spent on types that actually have parameters, and
bumped to 256 to clear the p99 of Base/stdlib signature slots (~160
expanded DataTypes) with some margin.

The sentinel needs a writer method to roundtrip: without one, a
parameters vector containing it would MethodError during the cache
write and degrade the whole module binding to Unserializable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pfitzseb
pfitzseb merged commit cf0e5c3 into julia-vscode:main Jul 6, 2026
21 checks passed
@PatrickHaecker
PatrickHaecker deleted the sp/bound-type-parameter-expansion branch July 7, 2026 03:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants