[education/rkward/releases/0.8.3] rkward/rbackend: Adapt to removal/rename of promise-handling functions
Thomas Friedrichsmeier
null at kde.org
Fri Apr 17 16:32:41 BST 2026
Git commit 9dbaa5068608e9e668491c32d80e86ebd931e905 by Thomas Friedrichsmeier.
Committed on 17/04/2026 at 15:31.
Pushed by tfry into branch 'releases/0.8.3'.
Adapt to removal/rename of promise-handling functions
M +10 -1 rkward/rbackend/rkrapi.cpp
M +20 -6 rkward/rbackend/rkrapi.h
M +1 -2 rkward/rbackend/rkrbackend.cpp
M +27 -6 rkward/rbackend/rkstructuregetter.cpp
M +1 -1 rkward/rbackend/rkstructuregetter.h
https://invent.kde.org/education/rkward/-/commit/9dbaa5068608e9e668491c32d80e86ebd931e905
diff --git a/rkward/rbackend/rkrapi.cpp b/rkward/rbackend/rkrapi.cpp
index 979b2b0ad..eb56a1c05 100644
--- a/rkward/rbackend/rkrapi.cpp
+++ b/rkward/rbackend/rkrapi.cpp
@@ -34,7 +34,16 @@ void RFn::init(void *libr_dll_handle, void *(*dlsym_fun)(void *, const char *))
// work around various incompatiblities between R versions
// R < 4.5.0
if (!R_ClosureFormals) {
- RK_DEBUG(RBACKEND, DL_DEBUG, "Falling back from R_ClosureFormals to FORMALS (%p)", R_ClosureFormals);
R_ClosureFormals = reinterpret_cast<decltype(R_ClosureFormals)>(dlsym_fun(libr_dll_handle, "FORMALS"));
+ RK_DEBUG(RBACKEND, DL_DEBUG, "Falling back from R_ClosureFormals to FORMALS (%p)", R_ClosureFormals);
}
+#if R_VERSION != R_Version(4, 6, 0)
+ if (!R_GetBindingType) {
+ PRCODE = reinterpret_cast<decltype(PRCODE)>(dlsym_fun(libr_dll_handle, "PRCODE"));
+ PRENV = reinterpret_cast<decltype(PRCODE)>(dlsym_fun(libr_dll_handle, "PRENV"));
+ PRVALUE = reinterpret_cast<decltype(PRCODE)>(dlsym_fun(libr_dll_handle, "PRVALUE"));
+ RK_DEBUG(RBACKEND, DL_DEBUG, "Falling back from R_GetBindingType to PRCODE (%p)", PRCODE);
+ RK_ASSERT(PRCODE);
+ }
+#endif
}
diff --git a/rkward/rbackend/rkrapi.h b/rkward/rbackend/rkrapi.h
index c3b144934..8e7ba33fe 100644
--- a/rkward/rbackend/rkrapi.h
+++ b/rkward/rbackend/rkrapi.h
@@ -177,16 +177,30 @@ class RFn : public QObject {
#if (R_VERSION >= R_Version(4, 5, 0))
IMPORT_R_API(R_ClosureFormals);
#else
- // will be handled, manually, in init
- static SEXP (*R_ClosureFormals)(SEXP);
+ // used to be named FORMALS; will be handled in init, manually
+ static inline SEXP (*R_ClosureFormals)(SEXP);
+#endif
+#if (R_VERSION >= R_Version(4, 6, 0))
+ IMPORT_R_API(R_GetBindingType);
+ //IMPORT_R_API(R_ActiveBindingFunction);
+ IMPORT_R_API(R_DelayedBindingExpression);
+ IMPORT_R_API(R_DelayedBindingEnvironment);
+ //IMPORT_R_API(R_ForcedBindingExpression);
+ IMPORT_R_API(R_getVar);
+ // these were removed in R 4.6, but we still need them
+ // if using a lower R at runtime
+ static inline SEXP (*PRCODE)(SEXP) = nullptr;
+ static inline SEXP (*PRENV)(SEXP) = nullptr;
+ static inline SEXP (*PRVALUE)(SEXP) = nullptr;
+#else
+ IMPORT_R_API(PRCODE);
+ IMPORT_R_API(PRENV);
+ IMPORT_R_API(PRVALUE);
#endif
IMPORT_R_API(INTEGER);
IMPORT_R_API(LOGICAL);
IMPORT_R_API(LENGTH);
- IMPORT_R_API(PRCODE);
- IMPORT_R_API(PRENV);
IMPORT_R_API(PRINTNAME);
- IMPORT_R_API(PRVALUE);
IMPORT_R_API(REAL);
IMPORT_R_API(R_CheckDeviceAvailable);
IMPORT_R_API(R_CheckStack);
@@ -293,7 +307,7 @@ class RFn : public QObject {
IMPORT_R_API(Riconv_close);
IMPORT_R_API(Riconv_open);
IMPORT_R_API(SET_TAG);
- IMPORT_R_API(SET_TYPEOF);
+ IMPORT_R_API(Rf_lcons);
IMPORT_R_API(STRING_ELT);
IMPORT_R_API(SET_STRING_ELT);
IMPORT_R_API(SET_VECTOR_ELT);
diff --git a/rkward/rbackend/rkrbackend.cpp b/rkward/rbackend/rkrbackend.cpp
index e69ec744c..190ad9f58 100644
--- a/rkward/rbackend/rkrbackend.cpp
+++ b/rkward/rbackend/rkrbackend.cpp
@@ -1207,8 +1207,7 @@ RCommandProxy *RKRBackend::runDirectCommand(const QString &command, RCommand::Co
static void setWarnOption(int level, bool tryeval = false) {
SEXP s, t;
- RFn::Rf_protect(t = s = RFn::Rf_allocList(2));
- RFn::SET_TYPEOF(s, LANGSXP);
+ RFn::Rf_protect(t = s = RFn::Rf_lcons(ROb(R_NilValue), RFn::Rf_allocList(2)));
RFn::SETCAR(t, RFn::Rf_install("options"));
t = RFn::CDR(t);
RFn::SETCAR(t, RFn::Rf_ScalarInteger(level));
diff --git a/rkward/rbackend/rkstructuregetter.cpp b/rkward/rbackend/rkstructuregetter.cpp
index d1f079431..a44161c62 100644
--- a/rkward/rbackend/rkstructuregetter.cpp
+++ b/rkward/rbackend/rkstructuregetter.cpp
@@ -125,19 +125,40 @@ void RKStructureGetter::getStructureSafe(SEXP value, const QString &name, int ad
}
}
-/** Temporarily resolve a promise, usually without keeping its value (unless keep_evalled_promises is set, which it never is, at the time of this writing).
+/** Get a symbol value from an environment, taking care not to (permanently) for promises (unless keep_evalled_promises is set,
+ * which it never is, at the time of this writing).
* This is useful for peeking into large objects while building the object tree, without permanently using lots of RAM.
*
* @note This is is not quite perfect, however. E.g. if we have two promises a and b, where b takes a slice out of a, then
* evaluating b will force a, permanently. */
-SEXP RKStructureGetter::resolvePromise(SEXP from, SEXP env) {
+SEXP RKStructureGetter::peekFromEnv(SEXP sym, SEXP env) {
RK_TRACE(RBACKEND);
+ if (keep_evalled_promises) {
+ return RFn::R_getVar(sym, env, /* inherits: */ FALSE);
+ }
+#if R_VERSION >= R_Version(4, 6, 0)
+ if (RFn::R_GetBindingType) { // implies R runtime >= 4.6
+ const auto type = RFn::R_GetBindingType(sym, env);
+ if (type == R_BindingTypeDelayed) {
+ RK_DEBUG(RBACKEND, DL_TRACE, "temporarily resolving delayed expression");
+ return RFn::Rf_eval(RFn::R_DelayedBindingExpression(sym, env), RFn::R_DelayedBindingEnvironment(sym, env));
+ } else if (type == R_BindingTypeMissing) {
+ return ROb(R_NilValue);
+ } else if (type == R_BindingTypeUnbound) {
+ return ROb(R_NilValue);
+ }
+ return RFn::R_getVar(sym, env, /* inherits: */ FALSE);
+ }
+#endif
+ if (!RFn::PRCODE) {
+ // We have neither the new nor the old promise handling functions?
+ // fall back to forcing
+ return RFn::R_getVar(sym, env, /* inherits: */ FALSE);
+ }
+ SEXP from = RFn::Rf_findVar(sym, env);
SEXP ret = from;
if (RFn::TYPEOF(from) == PROMSXP) {
- if (keep_evalled_promises) {
- return RFn::Rf_eval(ret, env);
- }
ret = RFn::PRVALUE(from);
if (ret == ROb(R_UnboundValue)) {
RK_DEBUG(RBACKEND, DL_TRACE, "temporarily resolving unbound promise");
@@ -369,7 +390,7 @@ void RKStructureGetter::getStructureWorker(SEXP val, const QString &name, int ad
for (int i = 0; i < childcount; ++i) {
SEXP current_childname = RFn::Rf_install(RFn::R_CHAR(RFn::STRING_ELT(childnames_s, i))); // ??? Why does simply using RFn::STRING_ELT(childnames_i, i) crash?
RFn::Rf_protect(current_childname);
- SEXP child = resolvePromise(RFn::Rf_findVar(current_childname, value), value);
+ SEXP child = peekFromEnv(current_childname, value);
RFn::Rf_protect(child);
getStructureSafe(child, childnames[i], 0, children[i], nesting_depth + 1);
diff --git a/rkward/rbackend/rkstructuregetter.h b/rkward/rbackend/rkstructuregetter.h
index 9b92bffa8..6dafc92a4 100644
--- a/rkward/rbackend/rkstructuregetter.h
+++ b/rkward/rbackend/rkstructuregetter.h
@@ -36,7 +36,7 @@ class RKStructureGetter {
/** needed to wrap things inside an R_ToplevelExec */
static void getStructureWrapper(GetStructureWorkerArgs *data);
void getStructureSafe(SEXP value, const QString &name, int add_type_flags, RData *storage, int nesting_depth);
- SEXP resolvePromise(SEXP from, SEXP env);
+ SEXP peekFromEnv(SEXP symb, SEXP env);
SEXP prefetch_fun(const char *name, bool from_base = true);
More information about the rkward-tracker
mailing list