Repository navigation
Conversation
Changelog💅 Polish
Semantic Diff2 commits · Full page Read fields of a variable-free object without copying its type426355d · cknitt + Claude · Nov 07, 2026, 10:06 UTC
When the identifier's type is generic and has no type variables, a copy would be identical, so the identifier keeps the type itself and only the field's type is instantiated. Looking up a field that the type has doesn't change it. Types with variables are still copied. ✨ Type the object of a field read through its own identifier helper41ad3b0 · cknitt + Claude · Nov 07, 2026, 19:05 UTC · new The benchmarks showed every expression allocating a little more (up to 0.17%), because the new optional argument of type_expect was captured by the closure it builds for each expression. Identifiers are now typed by type_ident, which the field read calls directly for a bare identifier, so type_expect is as before. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9259 +/- ##
=======================================
Coverage 81.74% 81.74%
=======================================
Files 457 457
Lines 65713 65729 +16
=======================================
+ Hits 53715 53731 +16
Misses 11998 11998
🚀 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-9259 |
Why:
Each `o["f"]` instantiated the whole type of `o`, and the .cmt file
stored every copy, so reading each field of an object with n fields
once cost O(n²): 800 fields allocated 77M words and took 0.87 s. They
now allocate 6.5M words and take 0.06 s.
When the identifier's type is generic and has no type variables, a copy
would be identical, so the identifier keeps the type itself and only the
field's type is instantiated. Looking up a field that the type has doesn't
change it. Types with variables are still copied.
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>
2537b0a to
426355d
Compare
🎉 Compiler benchmarksAllocated wordsIn total: 456,989,425 → 439,257,959 (-3.88%). 🎉 Improvement: 1 benchmark allocates more than 1% less.
InstructionsIn total: 10,226,962,471 → 9,405,550,072 (-8.03%). 🎉 Improvement: 1 benchmark executes more than 1% fewer instructions.
Pull request 41ad3b0 compared with base bf35764 in this run. |
The benchmarks showed every expression allocating a little more (up to 0.17%), because the new optional argument of type_expect was captured by the closure it builds for each expression. Identifiers are now typed by type_ident, which the field read calls directly for a bare identifier, so type_expect is as before. 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>
Part of #9256 (field access on a large object type).
Each
o["f"]instantiated the whole type ofo, and the.cmtfile stored every copy (writing it was the largest cost), so reading each field of an n-field object once was quadratic.When the identifier's type is generic and has no type variables, an instance would be an identical copy.
othen keeps the type itself, and only the field's type is instantiated, as before.filter_methoddoesn't change a type that has the field, and the shared type is never unified: the identifier isn't unified with the expected type, which is a fresh variable there. Types with variables are still copied. The check walks the type once and remembers the last type asked about, as reads of one object usually follow each other.Measured with
bscon linux-arm64, allocated words and wall time:What remains is the linear scan of the field list per read.
New test:
undefined_method_closed_objectreads an existing and a missing field of a toplevel object, so the error goes through the new path.🤖 Generated with Claude Code
https://claude.ai/code/session_01Kbf3gK1dcjbe97yY1wRMgs