Skip to content

fix bug in src/reassign.c - #640

Open
mpadge wants to merge 1 commit into
r-lib:mainfrom
mpadge:main
Open

fix bug in src/reassign.c#640
mpadge wants to merge 1 commit into
r-lib:mainfrom
mpadge:main

Conversation

@mpadge

@mpadge mpadge commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Credit here, like it or not, to Claude for uncovering this one. I used to also use SET_BODY in one package of mine. When R removed export of the SET_ functions from Rinternals.h, I looked here for how you got around it, and ended up wholesale copying your approach - with full attribution!

I encountered an odd bug in my code, and set Claude on a debugging session. It came up with this solution, which indeed works. This is an edited version of the Claude comment:


The struct copy in the C code wires 'new's formals/body/env directly into 'old_fun' without going through R's normal SET_FORMALS/SET_BODY/ SET_CLOENV setters. Those setters exist not just to assign the pointer, but also to run the generational-GC write barrier (so that an old-generation object such as 'old_fun', which may already have survived several collections, is flagged as now referencing potentially younger objects). Skipping that barrier is safe as long as the referenced objects can never be reclaimed while 'old_fun' still points to them. 'R_PreserveObject' is the public, API-stable way to guarantee that: it keeps an object alive for the remainder of the session regardless of the normal generational reachability scan. That permanently retains these (typically small) formals/body/env objects, which is an acceptable trade-off for a tracing tool to avoid the alternative: a GC collecting them out from under 'old_fun' the next time a minor collection runs, corrupting the traced function.

The struct copy in the C code  wires 'new's formals/body/env
directly into 'old_fun' without going through R's normal SET_FORMALS/SET_BODY/
SET_CLOENV setters. Those setters exist not just to assign the pointer, but
also to run the generational-GC write barrier (so that an old-generation object
such as 'old_fun', which may already have survived several collections, is
flagged as now referencing potentially younger objects). Skipping that barrier
is safe as long as the referenced objects can never be reclaimed while
'old_fun' still points to them. 'R_PreserveObject' is the public, API-stable
way to guarantee that: it keeps an object alive for the remainder of the
session regardless of the normal generational reachability scan. That
permanently retains these (typically small) formals/body/env objects, which is
an acceptable trade-off for a tracing tool to avoid the alternative: a GC
collecting them out from under 'old_fun' the next time a minor collection runs,
corrupting the traced function.
@CLAassistant

CLAassistant commented Jul 16, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

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.

2 participants