Skip to content

Update environment binding API with planned ctors and accessors#1871

Merged
lionel- merged 11 commits intor-lib:mainfrom
lionel-:feature/c-env-api
Feb 5, 2026
Merged

Update environment binding API with planned ctors and accessors#1871
lionel- merged 11 commits intor-lib:mainfrom
lionel-:feature/c-env-api

Conversation

@lionel-
Copy link
Member

@lionel- lionel- commented Feb 4, 2026

So we can write algorithms that can be easily switched to the API discussed at https://bugs.r-project.org/show_bug.cgi?id=18928

Copy link
Member

@DavisVaughan DavisVaughan left a comment

Choose a reason for hiding this comment

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

Looks like a nice API to me!

Comment on lines +140 to +150
r_obj* value = r_env_find(env, sym);

if (value == r_syms.unbound) {
return R_ENV_BINDING_TYPE_unbound;
}

if (value == r_missing_arg) {
return R_ENV_BINDING_TYPE_missing;
}

if (r_typeof(value) == R_TYPE_promise) {
Copy link
Member

Choose a reason for hiding this comment

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

I wouldn't be against implementing these in terms of just pure R API, rather than r_env_find(). That way we could fully remove r_env_find() eventually without having to think about "old R" support

Copy link
Member Author

Choose a reason for hiding this comment

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

hmm I might be misunderstanding but I'm not sure how that would work. I'm fine having the old R support in place, this code will likely be unchanged for years once the R API has stabilised.

Copy link
Member

Choose a reason for hiding this comment

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

all i meant here was using Rf_findVarInFrame3() directly in place of r_env_find(), in case we wanted to remove r_env_find()

Comment on lines +167 to +168
r_env_unbind(env, sym);
R_MakeActiveBinding(sym, fn, env);
Copy link
Member

Choose a reason for hiding this comment

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

Interesting that R_MakeActiveBinding() fails on preexisting standard bindings

r_abort("Not a forced binding.");
}

return PRVALUE(value);
Copy link
Member

Choose a reason for hiding this comment

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

I faintly remember us saying that you are supposed to just Rf_eval() a forced binding to get its value, which is why we didn't add R_ForcedBindingValue(). Maybe use that?

Copy link
Member Author

Choose a reason for hiding this comment

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

We have the promise already so doing a separate lookup might be surprising in other ways

Copy link
Member

Choose a reason for hiding this comment

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

But PRVALUE() is non-API. So what's the correct API compliant way to get the value?

Comment on lines +28 to +30
KEEP(value);
Rf_defineVar(sym, value, env);
FREE(1);
Copy link
Member

Choose a reason for hiding this comment

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

From what I can tell, Rf_defineVar() will protect value if it needs to, so I think you can skip this

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure but rchk might take issue with that, I faintly remember having to do macros to get it to acknowledge the protection.

Copy link
Member Author

Choose a reason for hiding this comment

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

yep: 28ce7b0

}

static inline
void r_env_poke(r_obj* env, r_obj* sym, r_obj* value) {
Copy link
Member

Choose a reason for hiding this comment

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

For legacy reasons? Seems like we use it a lot

Copy link
Member Author

Choose a reason for hiding this comment

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

I missed that, I have now renamed it to r_env_bind()

Comment on lines +99 to +109
r_env_binding_delayed_expr(from, sym),
r_env_binding_delayed_env(from, sym)
);
break;

case R_ENV_BINDING_TYPE_forced:
r_env_bind_forced(
env,
sym,
r_env_binding_forced_expr(from, sym),
r_env_binding_forced_value(from, sym)
Copy link
Member

Choose a reason for hiding this comment

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

I guess this relies on the _expr, _env, and _value helpers never allocating, right? Because if r_env_binding_delayed_expr() and r_env_binding_delayed_env() allocate then we'd have a protection issue. But I guess we'd say that the promise is protecting them?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think you're right, this relies on the assumption that the accessors do pure access and don't allocate. Otherwise rchk will flag us down.

Copy link
Member

@DavisVaughan DavisVaughan left a comment

Choose a reason for hiding this comment

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

Looks like a nice API to me!

@lionel- lionel- merged commit fbf75dc into r-lib:main Feb 5, 2026
13 checks passed
@lionel- lionel- deleted the feature/c-env-api branch February 5, 2026 17:25
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