Pin the real GC ref-count contract + correct the GC reference docs#79
Merged
Conversation
GarbageCollectionTest.bb had only two trivial asserts and did not exercise the ref-count contract the language reference cites. Expand it with blocks pinning the EMPIRICALLY-VERIFIED behavior (distinct Type per concern to isolate the per-type object lists): - aliasing a second local to an object does NOT raise RefCount (it tracks the lifetime binding, not every alias); - a field-reference cycle (a\other=b, b\other=a) is COLLECTED at scope exit -- it does not leak, because field assignments add no references; - an object referenced only through a live object's field stays alive/readable. Values captured from the live runtime, not assumed. No src change -- this pins current behavior (the deeper reference_map vs obj->ref_cnt question is a separate, deferred investigation). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes two inaccurate claims I shipped in #67's modern-language reference, now verified against the runtime: - "RefCount reports how many references currently keep an object alive" implied it counts every alias; it does not (aliasing/field assignment don't increment it). - "reference cycles are not collected automatically -- break the cycle yourself" is false here: field back-references add no counts, so a cycle is collected when its holding variables leave scope. Reword to describe the real contract (count follows the lifetime binding; objects freed when the holding variable leaves scope; keep a live variable to retain something) and point the citation at the now-demonstrating GarbageCollectionTest.bb. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Empirical findings (RefCount doesn't count aliases/field-refs; cycles collected; field-referenced objects survive), the doc corrections, and the deferred reference_map-vs-obj->ref_cnt soundness question. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Non-technical summary
The language reference's garbage-collection section (shipped in PR #67) made two claims that don't match how the runtime actually behaves, and cited a test that didn't prove them. This PR corrects the docs to the empirically-verified behavior and expands the cited test so it genuinely demonstrates the contract. Fixes a shipped doc-correctness defect (my own from #67) and turns the GC test from two trivial asserts into a real contract.
Technical summary
Verified against the live runtime (scratch
.bbprobes), then pinned:RefCounttracks the object's lifetime binding, not every alias —Local b.T = adoes not raiseRefCount(a)(stays 1); field assignments aren't counted either.a\other=b : b\other=a(function locals) → after scope exitFirst T = Null,Each Tcount 0. (The field back-references add no counts, so the cycle holds nothing alive.)container\other\tagreadable).Changes:
tests/GarbageCollectionTest.bb: keeptestRef/testRel; addtestAliasDoesNotIncrementRefCount,testReferenceCycleIsCollected,testFieldReferencedObjectStaysAlive(oneTypeper concern to isolate per-type object lists).help/language/lang_ref_modern.html: correct the two inaccurate claims and repoint the citation at the now-demonstrating test.src/change — this pins current behavior. The deeperreference_map-vs-obj->ref_cntmechanism question (should aliases/fields be counted?) is flagged as a separate, deferred investigation; this PR does not change runtime semantics.Acceptance criteria + results
Testblocks pass underblitzcc -t, asserting empirically-captured values (single=1, alias stays 1, cycle collected → count 0 /First = Null, field-ref object survives → count 2).RefCountcounts all aliases, no longer claims cycles leak; theGarbageCollectionTest.bbcitation is now accurate.test.batgreen; corpus sweep unaffected; nosrc/change.Trade-offs, deferred follow-ups
reference_mapref-count should track aliases/field references (thereference_mapvs olderobj->ref_cntdistinction) — a potential GC-soundness/design question needing a dedicated investigation, not a rushed runtime change.SoundPan/ChannelPanon the OpenAL backend (corpus Class B + shipped-doc gap); a TCP-socket UAF inbbsockets.cpp; contextual-keyword Phase 2.🤖 Generated with Claude Code