Skip to content

Pin the real GC ref-count contract + correct the GC reference docs#79

Merged
CoreyRDean merged 3 commits into
developfrom
test/gc-refcount-contract
May 30, 2026
Merged

Pin the real GC ref-count contract + correct the GC reference docs#79
CoreyRDean merged 3 commits into
developfrom
test/gc-refcount-contract

Conversation

@CoreyRDean
Copy link
Copy Markdown
Collaborator

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 .bb probes), then pinned:

  • RefCount tracks the object's lifetime binding, not every aliasLocal b.T = a does not raise RefCount(a) (stays 1); field assignments aren't counted either.
  • A reference cycle is collected, not leakeda\other=b : b\other=a (function locals) → after scope exit First T = Null, Each T count 0. (The field back-references add no counts, so the cycle holds nothing alive.)
  • A field-only-referenced object stays alive while its owner is reachable (count 2; container\other\tag readable).

Changes:

  • tests/GarbageCollectionTest.bb: keep testRef/testRel; add testAliasDoesNotIncrementRefCount, testReferenceCycleIsCollected, testFieldReferencedObjectStaysAlive (one Type per 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.
  • No src/ change — this pins current behavior. The deeper reference_map-vs-obj->ref_cnt mechanism question (should aliases/fields be counted?) is flagged as a separate, deferred investigation; this PR does not change runtime semantics.

Acceptance criteria + results

  • New Test blocks pass under blitzcc -t, asserting empirically-captured values (single=1, alias stays 1, cycle collected → count 0 / First = Null, field-ref object survives → count 2).
  • Doc no longer claims RefCount counts all aliases, no longer claims cycles leak; the GarbageCollectionTest.bb citation is now accurate.
  • test.bat green; corpus sweep unaffected; no src/ change.

Trade-offs, deferred follow-ups

  • Rejected an idealized ref-count suite (aliasing→RefCount=2): the runtime doesn't behave that way; pinned reality instead (the empirical-capture pattern).
  • Deferred (real open question): whether the reference_map ref-count should track aliases/field references (the reference_map vs older obj->ref_cnt distinction) — a potential GC-soundness/design question needing a dedicated investigation, not a rushed runtime change.
  • Other deferred recon pitches this iteration: restore SoundPan/ChannelPan on the OpenAL backend (corpus Class B + shipped-doc gap); a TCP-socket UAF in bbsockets.cpp; contextual-keyword Phase 2.

🤖 Generated with Claude Code

CoreyRDean and others added 3 commits May 30, 2026 01:32
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>
@CoreyRDean CoreyRDean requested a review from a team as a code owner May 30, 2026 06:33
@CoreyRDean CoreyRDean added the documentation Improvements or additions to documentation label May 30, 2026
@CoreyRDean CoreyRDean merged commit f9af180 into develop May 30, 2026
4 checks passed
@CoreyRDean CoreyRDean deleted the test/gc-refcount-contract branch May 30, 2026 06:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant