Repository navigation
Conversation
Changelog💅 Polish
Semantic Diff1 commit · Full page Expand chains of type aliases without repeated occur checks6a1750e · cknitt + Claude · Nov 07, 2026, 10:05 UTC
Binding the fresh parameters of an expanded alias skips the occur check, since nothing can contain them yet. The constraint check skips type constructors whose parameters are distinct variables, which accept any arguments, and the cycle check forgets the expansions it memorized. Review
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9257 +/- ##
=======================================
Coverage 81.74% 81.74%
=======================================
Files 457 457
Lines 65713 65736 +23
=======================================
+ Hits 53715 53737 +22
- Misses 11998 11999 +1
🚀 New features to boost your workflow:
|
rescript
@rescript/belt
@rescript/darwin-arm64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-9257 |
Why:
Each alias in `type t1<'a> = t0<('a, int)>`, `type t2<'a> = t1<('a, int)>`,
… was checked by expanding the whole chain several times, and every
expansion step ran occur checks over arguments that grow along the
chain: 200 aliases allocated 850M words and took 1.6 s. They now
allocate 19M words and take 0.15 s.
Binding the fresh parameters of an expanded alias skips the occur check,
since nothing can contain them yet. The constraint check skips type
constructors whose parameters are distinct variables, which accept any
arguments, and the cycle check forgets the expansions it memorized.
Review: What remains superlinear? Expansions memorize each alias in one
list shared along the chain, and looking an alias up scans it, a small
cubic term: 800 aliases take 6.7 s, 290M words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kbf3gK1dcjbe97yY1wRMgs
Signed-off-by: Christoph Knittel <ck@cca.io>
5546f81 to
6a1750e
Compare
🎉 Compiler benchmarksAllocated wordsIn total: 456,989,425 → 435,343,562 (-4.74%). 🎉 Improvement: 17 benchmarks allocate more than 1% less.
99 more changes
InstructionsIn total: 10,226,961,817 → 9,754,934,599 (-4.62%). 🎉 Improvement: 4 benchmarks execute more than 1% fewer instructions.
2 more changes
Pull request 6a1750e compared with base bf35764 in this run. |
Part of #9256 (chains of type aliases).
A chain like
type t1<'a> = t0<('a, int)>,type t2<'a> = t1<('a, int)>, … was cubic: each new alias is checked by expanding the whole chain several times, and every expansion step ran occur checks over arguments that grow along the chain.Ctype.substbinds the freshly instantiated parameters of an expanded alias without an occur check, when they are distinct unlinked variables that nothing can contain yet. Constrained or non-generic parameters keep the full check.Typedecl.check_constraints_recskips type constructors whose parameters are distinct variables, since they accept any arguments. Matching against the constrained instance expanded both sides fully.Ctype.cyclic_abbrevforgets the expansions it memorized, so that generalizing the declaration doesn't walk the whole chain again, and keeps the types it has seen in a set.Measured with
bscon linux-arm64, allocated words and wall time:What remains: expansions memorize each alias in one list shared along the chain, and lookups scan it, so 800 aliases still take 6.7 s (290M words). Fixing that needs a different memo structure.
New test:
constraint_failed_recursivechecks that a constraint is still enforced through a recursive group, where onlycheck_constraints_recsees it.🤖 Generated with Claude Code
https://claude.ai/code/session_01Kbf3gK1dcjbe97yY1wRMgs