Skip to content

Conversation

@DavisVaughan
Copy link
Member

@DavisVaughan DavisVaughan commented Oct 23, 2025

Part of #1933
Closes #1940

Uses Rf_isS4() and Rf_asS4() from
https://github.com/wch/r-source/blob/8d57e7515089387414c75df502f3f0c0e2c022e5/src/main/objects.c#L1838-L1878

Most important comments are pointed out below as code comments

Comment on lines -274 to 297

static inline SEXP r_mark_s4(SEXP x) {
SET_S4_OBJECT(x);
return(x);
static inline bool r_is_s4(SEXP x) {
return Rf_isS4(x);
}
static inline SEXP r_unmark_s4(SEXP x) {
UNSET_S4_OBJECT(x);
return(x);
static inline SEXP r_as_s4(SEXP x) {
// - Return value must be used, unlike `SET_S4_OBJECT()`
// - `Rf_asS4()` calls `shallow_duplicate(x)` if `MAYBE_SHARED(x)`
// - `flag = 1` goes through `SET_S4_OBJECT()`
// - `complete` is never utilized when `flag = 1`
const Rboolean flag = 1;
const int complete = 0;
return Rf_asS4(x, flag, complete);
}
static inline SEXP r_as_not_s4(SEXP x) {
// - Return value must be used, unlike `UNSET_S4_OBJECT()`
// - `Rf_asS4()` calls `shallow_duplicate(x)` if `MAYBE_SHARED(x)`
// - `flag = 0` goes through `UNSET_S4_OBJECT()`
// - `complete` is for S4 objects that wrap a "complete" S3 object by placing
// it in the `.Data` slot. If you set `complete = 1`, it will unwrap and
// return that, which we don't want. If `complete = 0`, no additional
// behavior will happen beyond the `UNSET_S4_OBJECT()` call.
const Rboolean flag = 0;
const int complete = 0;
return Rf_asS4(x, flag, complete);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the family of 3 S4 utilities we need

The main points are:

  • These are no longer "mark" functions. The return value actually does matter. I have updated the names and call sites accordingly.
  • Rf_asS4(), used for both setting and unsetting the S4 bit, will shallow clone x if it is shared. Where we use it in vec_restore_default() we have freshly cloned x already with vec_clone_referenced(x, ownership) so it should not look shared unless we provided VCTRS_OWNERSHIP_deep and x was somehow shared already, even though we claimed to own it completely. There is nothing we can do about this since we don't have access to SET_S4_OBJECT() now. I don't know how common this would be.
  • complete is not anything we need to worry about, we just set it to 0 and it is never used.


if (is_s4) {
r_mark_s4(x);
x = r_as_s4(x);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not protecting because we are on the way out here

@DavisVaughan DavisVaughan requested a review from lionel- October 23, 2025 18:44
@DavisVaughan DavisVaughan merged commit e02b44f into main Oct 24, 2025
13 checks passed
@DavisVaughan DavisVaughan deleted the feature/s4-compliance branch October 24, 2025 13:31
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