Repository navigation
rc: a method bound through an interface, or by a global's initializer, counts its object - #659
Merged
Merged
Conversation
…, counts its object Under rc a bound method counts the object it is bound to through that object's type tag, and only the bound value's receivers take a count (getBoundObjectTypeTag). Two holes: - bound through an interface (`c = ig.get`), the object's type is not known where the bound value is built, so it got no tag and counted nothing: `c()` ran on a freed object once the interface went. It now takes the tag the interface carries for what it points at (null for a missing optional method). - a global initialized with a bound method (`let c = mk().get`, `let c = G.v.get`) took no count: a temporary object was given back at the end of the initializer, and the first `c = d` released it a second time. The global now takes one, as it does for a field read. Fuzzer (#646), found past the first 1000 programs (40181). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tagged bound value was an UnrealizedConversionCast over a ts.CreateBoundFunction still waiting for its own conversion. Under --opt that corrupted the compiler's heap on Linux (36interfaces, 00owned_construct_interface). Build the struct directly, as CreateBoundFunctionOpLowering does: method, this, then the tag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ounts-object # Conflicts: # tslang/test/tester/CMakeLists.txt
…ounts-object # Conflicts: # tslang/test/tester/CMakeLists.txt
…ounts-object # Conflicts: # tslang/test/tester/CMakeLists.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes fuzzer repro 40181 (#646), found in a fresh range past the first 1000 programs. It fails on main as well.
Why
Under rc a bound method counts the object it is bound to through that object's type tag at
CLOSURE_TYPE_INDEX. Only the bound value's receivers take a count (getBoundObjectTypeTag). There were two holes:c = ig.get): the object's type isn't known where the bound value is built, so the value got no tag and counted nothing.c()then ran on a freed object once the interface went away.let c = mk().get,let c = G.v.get): the global took no count. A temporary object was given back at the end of the initializer, and the firstc = dreleased it a second time.Fix
InterfaceSymbolRefOpLowering, rc only: the bound value takes the tag the interface already carries for what it points at (INTERFACE_TYPE_INDEX, set byNewInterfaceOpLowering). A missing optional method has a nullthis, and its tag is null too. The struct is built from LLVM values directly, the wayCreateBoundFunctionOpLoweringbuilds it. A first version wrapped a not-yet-convertedts.CreateBoundFunctionin anUnrealizedConversionCast. With that version, tslang aborted in malloc under--opton Linux (36interfaces, 00owned_construct_interface). Building the struct directly, in the second commit, makes both pass. I didn't pin down exactly what corrupted the heap.claimGlobalInitializers: a global initialized with a bound method takes a count, as it already does for a field read.Tests
tests/00bound_method_counts_object.tscovers:It runs compiled, under JIT, and in the rc/none corpus.
Verification
ctest -C Release: Windows 5278/5278 with main merged at 149646d (rc: a new array given straight to a call is held across it #649). The later merge of rc: a value stored into a local that owns nothing is not freed under it #652 was not rerun on Windows. Its code is in the combined Linux run below, and CI covers the final head.🤖 Generated with Claude Code