Skip to content

rc: a const interface or bound method made over a field read holds a count - #642

Merged
ASDAlexander77 merged 2 commits into
mainfrom
fix-rc-const-bound-field-read
Oct 22, 2026
Merged

ASDAlexander77 merged 2 commits into
mainfrom
fix-rc-const-bound-field-read

Conversation

@ASDAlexander77

@ASDAlexander77 ASDAlexander77 commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

const m = h.v.get and const i: I = h.v were folded into the view itself and held no reference of their own. Under rc, h.v = d then freed the object that m() / i.get() went on to use: a crash, while let m = h.v.get worked. #637 gave such a const storage only when its object is read out of a local's slot (viewsLocalSlot), and #640 made a kept bound method count its object. A view made over a field read was not covered.

Under rc, an interface or bound method made over a field read now gets storage like a let, the same as a const read out of a field (#635). viewsLocalSlot is split into viewedValue, the object the view is made over, so the field rule can reuse it. Under own the folded read stays a borrow.

An element-rooted view (const m = arr[0].get) depends on the element rule in #641. Once both PRs are in, covering it is a one-line follow-up: readsElement(viewedValue(initial)).

Tests

  • 00const_view_of_field.ts covers a bound method and an interface view under churn. It is in the default compile/jit lists and in TSLANG_CORPUS (rc + none). On main it crashes under rc.
  • Teeth: dropping readsField(viewedValue(..)) fails test-compile-rc-corpus-00const-view-of-field (and its jit twin). Without the switch, it passes.
  • Windows ctest -C Release: 4920/4920. WSL Linux full run: 4902/4902.

Merge note: this and #641 both edit the rc condition line and the comment paragraph in processConstRef (MLIRGenImpl.h), so the second to merge needs a small rebase.

🤖 Generated with Claude Code

…count

`const m = h.v.get` and `const i: I = h.v` were folded into the view
itself, holding no reference of their own, so `h.v = d` freed the object
`m()` and `i.get()` then ran on (a crash under rc; `let m = h.v.get` was
fine). #637 gave such a const storage when its object is read out of a
local's slot, and #640 made a kept bound method count its object; a view
over a field read was left out. Under rc it now gets storage like a `let`,
as a const read out of a field already does (#635). Under own the folded
read stays a borrow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both changed the rc const rule in processConstRef: it now takes a field
read, an element read (#641) and a view over a field read (this PR).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 92c0ca5 into main Oct 22, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-rc-const-bound-field-read branch October 22, 2026 18:59
ASDAlexander77 added a commit that referenced this pull request Oct 22, 2026
… a count (#644)

`const m = arr[0].get` and `const i: I = arr[1]` were folded into the view
itself, holding no reference of their own, so `arr[0] = d` freed the object
`m()` and `i.get()` then ran on. #641 gave a const read out of an element
storage and #642 gave a view over a field read the same; a view over an
element read is the remaining case. Under rc it now gets storage like a
`let`. Under own the folded read stays a borrow.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ASDAlexander77 added a commit that referenced this pull request Oct 23, 2026
…global a count (#647)

Two shapes the memory-model fuzzer (#646) found double-freeing under rc
since #639.

- holdGlobalArguments retained the argument right before the call. A
  later argument of the same call could give the global a new value
  first (`f(G, (G = mk(), x))`, `f(H.n, (H.n = mk(), x))`): its release
  freed the block, and the hold then retained and released a freed
  block. The retain (and the #645 unwind slot) now follow the read.
- `const n = G` was folded into the read, holding no count. Once the
  global got a new value, here or in a call, `n` dangled (fuzzer R1).
  #639 made that a crash: `run(n.v)`, with `run` assigning G, held and
  released a field of the freed block. Under rc a const read out of a
  global, or an interface or bound method made over one, now gets
  storage like a const read out of a field or an element (#635, #641,
  #642, #644). Under own the folded read stays a borrow.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant