From 44e6a1a07298a29a0fd76a90977fd8574f983db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Sun, 29 Mar 2026 13:09:29 +0200 Subject: [PATCH 1/6] remove unneeded check using non-API R_UnboundValue --- inst/include/Rcpp/api/meat/Rcpp_eval.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/inst/include/Rcpp/api/meat/Rcpp_eval.h b/inst/include/Rcpp/api/meat/Rcpp_eval.h index 340d2ac7a..e2fcab35e 100644 --- a/inst/include/Rcpp/api/meat/Rcpp_eval.h +++ b/inst/include/Rcpp/api/meat/Rcpp_eval.h @@ -56,10 +56,6 @@ inline SEXP Rcpp_eval(SEXP expr, SEXP env) { // 'identity' function used to capture errors, interrupts Shield identity(Rf_findFun(::Rf_install("identity"), R_BaseNamespace)); - if (identity == R_UnboundValue) { - stop("Failed to find 'base::identity()'"); - } - // define the evalq call -- the actual R evaluation we want to execute Shield evalqCall(Rf_lang3(::Rf_install("evalq"), expr, env)); From ebf1f89d0dceb881ca1a0334b68e6e0b92f78eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Sun, 29 Mar 2026 13:15:54 +0200 Subject: [PATCH 2/6] use R_NilValue with R_getVarEx to avoid non-API R_UnboundValue + some refactoring --- inst/include/Rcpp/Environment.h | 54 ++++++++------------------------- 1 file changed, 12 insertions(+), 42 deletions(-) diff --git a/inst/include/Rcpp/Environment.h b/inst/include/Rcpp/Environment.h index 0f6f79928..2a9f879b5 100644 --- a/inst/include/Rcpp/Environment.h +++ b/inst/include/Rcpp/Environment.h @@ -95,20 +95,8 @@ namespace Rcpp{ * @return a SEXP (possibly R_NilValue) */ SEXP get(const std::string& name) const { - SEXP env = Storage::get__() ; - SEXP nameSym = Rf_install(name.c_str()); -#if R_VERSION < R_Version(4,5,0) - SEXP res = Rf_findVarInFrame( env, nameSym ) ; -#else - SEXP res = R_getVarEx(nameSym, env, FALSE, R_UnboundValue); -#endif - if( res == R_UnboundValue ) return R_NilValue ; - - /* We need to evaluate if it is a promise */ - if( TYPEOF(res) == PROMSXP){ - res = internal::Rcpp_eval_impl( res, env ) ; // #nocov - } - return res ; + Symbol nameSym = Rf_install(name.c_str()); + return get(nameSym); } /** @@ -122,12 +110,10 @@ namespace Rcpp{ SEXP env = Storage::get__() ; #if R_VERSION < R_Version(4,5,0) SEXP res = Rf_findVarInFrame( env, name ) ; + if (res == R_UnboundValue) return R_NilValue; #else - SEXP res = R_getVarEx(name, env, FALSE, R_UnboundValue); + SEXP res = R_getVarEx(name, env, FALSE, R_NilValue); #endif - - if( res == R_UnboundValue ) return R_NilValue ; - /* We need to evaluate if it is a promise */ if( TYPEOF(res) == PROMSXP){ res = internal::Rcpp_eval_impl( res, env ) ; @@ -144,21 +130,8 @@ namespace Rcpp{ * */ SEXP find( const std::string& name) const{ - SEXP env = Storage::get__() ; - SEXP nameSym = Rf_install(name.c_str()); -#if R_VERSION < R_Version(4,5,0) - SEXP res = Rf_findVar( nameSym, env ) ; -#else - SEXP res = R_getVarEx(nameSym, env, TRUE, R_UnboundValue); -#endif - - if( res == R_UnboundValue ) throw binding_not_found(name) ; - - /* We need to evaluate if it is a promise */ - if( TYPEOF(res) == PROMSXP){ - res = internal::Rcpp_eval_impl( res, env ) ; - } - return res ; + Symbol nameSym = Rf_install(name.c_str()); + return find(nameSym); } /** @@ -171,15 +144,11 @@ namespace Rcpp{ SEXP env = Storage::get__() ; #if R_VERSION < R_Version(4,5,0) SEXP res = Rf_findVar( name, env ) ; + if (res == R_UnboundValue) throw binding_not_found(name.c_str()); #else - SEXP res = R_getVarEx(name, env, TRUE, R_UnboundValue); + SEXP res = R_getVarEx(name, env, TRUE, R_NilValue); + if (res == R_NilValue) throw binding_not_found(name.c_str()); #endif - if( res == R_UnboundValue ) { - // Pass on the const char* to the RCPP_EXCEPTION_CLASS's - // const std::string& requirement - throw binding_not_found(name.c_str()) ; - } - /* We need to evaluate if it is a promise */ if( TYPEOF(res) == PROMSXP){ res = internal::Rcpp_eval_impl( res, env ) ; @@ -199,10 +168,11 @@ namespace Rcpp{ SEXP nameSym = Rf_install(name.c_str()); #if R_VERSION < R_Version(4,5,0) SEXP res = Rf_findVarInFrame( Storage::get__() , nameSym ) ; + return res != R_UnboundValue; #else - SEXP res = R_getVarEx(nameSym, Storage::get__(), FALSE, R_UnboundValue); + SEXP res = R_getVarEx(nameSym, Storage::get__(), FALSE, R_NilValue); + return res != R_NilValue; #endif - return res != R_UnboundValue ; } /** From e90460215123b3f3abee5be5ec7703bb65998072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Sun, 29 Mar 2026 13:20:34 +0200 Subject: [PATCH 3/6] use R_NilValue instead of non-API R_UnboundValue --- inst/include/Rcpp/Function.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inst/include/Rcpp/Function.h b/inst/include/Rcpp/Function.h index 783318b39..4248f530c 100644 --- a/inst/include/Rcpp/Function.h +++ b/inst/include/Rcpp/Function.h @@ -72,9 +72,9 @@ namespace Rcpp{ #if R_VERSION < R_Version(4,5,0) Shield env(Rf_findVarInFrame(R_NamespaceRegistry, Rf_install(ns.c_str()))); #else - Shield env(R_getVarEx(Rf_install(ns.c_str()), R_NamespaceRegistry, FALSE, R_UnboundValue)); + Shield env(R_getVarEx(Rf_install(ns.c_str()), R_NamespaceRegistry, FALSE, R_NilValue)); #endif - if (env == R_UnboundValue) { + if (env == R_NilValue) { stop("there is no namespace called \"%s\"", ns); } get_function(name, env); From d6b08fc8ad3aa1266b2af8450060ad6f6223113e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Sun, 29 Mar 2026 14:28:43 +0200 Subject: [PATCH 4/6] use R_BindingTypeUnbound instead of non-API R_UnboundValue --- inst/include/Rcpp/Promise.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/inst/include/Rcpp/Promise.h b/inst/include/Rcpp/Promise.h index 8b4096a01..9afa253de 100644 --- a/inst/include/Rcpp/Promise.h +++ b/inst/include/Rcpp/Promise.h @@ -48,13 +48,18 @@ namespace Rcpp{ * Return the result of the PRVALUE macro on the promise */ SEXP value() const{ - SEXP val = PRVALUE( Storage::get__() ) ; - if( val == R_UnboundValue ) throw unevaluated_promise() ; - return val ; + if (!was_evaluated()) throw unevaluated_promise(); + return PRVALUE(Storage::get__()); } bool was_evaluated() const { +#if R_VERSION < R_Version(4,6,0) return PRVALUE(Storage::get__()) != R_UnboundValue ; +#else + SEXP env = environment(); + R_BindingType_t bt = R_GetBindingType(Storage::get__(), env); + return bt != R_BindingTypeUnbound; +#endif } /** From 505eb9cacc0b7cdf8d3140bed982dec2019ee56b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Sun, 29 Mar 2026 14:31:51 +0200 Subject: [PATCH 5/6] update ChangeLog --- ChangeLog | 8 ++++++++ inst/include/Rcpp/Environment.h | 3 ++- inst/include/Rcpp/Function.h | 3 ++- inst/include/Rcpp/Promise.h | 3 ++- inst/include/Rcpp/api/meat/Rcpp_eval.h | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index a841bc970..51fbc8117 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2026-03-29 Iñaki Ucar + + * inst/include/Rcpp/api/meat/Rcpp_eval.h: Remove check for non-API + R_UnboundValue, which is never returned anyway from Rf_findFun + * inst/include/Rcpp/Function.h: Use alternative to R_UnboundValue + * inst/include/Rcpp/Promise.h: Idem + * inst/include/Rcpp/Environment.h: Idem + some refactoring + 2026-03-26 Dirk Eddelbuettel * inst/bib/Rcpp.bib: Refreshed a few more references diff --git a/inst/include/Rcpp/Environment.h b/inst/include/Rcpp/Environment.h index 2a9f879b5..2fc1e5223 100644 --- a/inst/include/Rcpp/Environment.h +++ b/inst/include/Rcpp/Environment.h @@ -2,7 +2,8 @@ // Environment.h: Rcpp R/C++ interface class library -- access R environments // // Copyright (C) 2009-2013 Dirk Eddelbuettel and Romain François -// Copyright (C) 2014-2026 Dirk Eddelbuettel, Romain François and Kevin Ushey +// Copyright (C) 2014-2025 Dirk Eddelbuettel, Romain François and Kevin Ushey +// Copyright (C) 2026 Dirk Eddelbuettel, Romain François, Kevin Ushey and Iñaki Ucar // // This file is part of Rcpp. // diff --git a/inst/include/Rcpp/Function.h b/inst/include/Rcpp/Function.h index 4248f530c..710649036 100644 --- a/inst/include/Rcpp/Function.h +++ b/inst/include/Rcpp/Function.h @@ -1,7 +1,8 @@ // Function.h: Rcpp R/C++ interface class library -- functions (also primitives and builtins) // -// Copyright (C) 2010 - 2026 Dirk Eddelbuettel and Romain Francois +// Copyright (C) 2010 - 2025 Dirk Eddelbuettel and Romain Francois +// Copyright (C) 2026 Dirk Eddelbuettel, Romain François and Iñaki Ucar // // This file is part of Rcpp. // diff --git a/inst/include/Rcpp/Promise.h b/inst/include/Rcpp/Promise.h index 9afa253de..01bec2978 100644 --- a/inst/include/Rcpp/Promise.h +++ b/inst/include/Rcpp/Promise.h @@ -2,7 +2,8 @@ // // Promise.h: Rcpp R/C++ interface class library -- promises (PROMSXP) // -// Copyright (C) 2010 - 2013 Dirk Eddelbuettel and Romain Francois +// Copyright (C) 2010 - 2025 Dirk Eddelbuettel and Romain François +// Copyright (C) 2026 Dirk Eddelbuettel, Romain François and Iñaki Ucar // // This file is part of Rcpp. // diff --git a/inst/include/Rcpp/api/meat/Rcpp_eval.h b/inst/include/Rcpp/api/meat/Rcpp_eval.h index e2fcab35e..6bd75e253 100644 --- a/inst/include/Rcpp/api/meat/Rcpp_eval.h +++ b/inst/include/Rcpp/api/meat/Rcpp_eval.h @@ -1,5 +1,5 @@ // Copyright (C) 2013 - 2025 Romain Francois -// Copyright (C) 2026 Romain Francois and Dirk Eddelbuettel +// Copyright (C) 2026 Romain Francois, Dirk Eddelbuettel and Iñaki Ucar // // This file is part of Rcpp. // From 3f35dbba635479836a7e4e915334a88fdb197daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Sun, 29 Mar 2026 14:56:50 +0200 Subject: [PATCH 6/6] only need to check for PROMSXP in the old path --- inst/include/Rcpp/Environment.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/inst/include/Rcpp/Environment.h b/inst/include/Rcpp/Environment.h index 2fc1e5223..344f25d6f 100644 --- a/inst/include/Rcpp/Environment.h +++ b/inst/include/Rcpp/Environment.h @@ -112,13 +112,11 @@ namespace Rcpp{ #if R_VERSION < R_Version(4,5,0) SEXP res = Rf_findVarInFrame( env, name ) ; if (res == R_UnboundValue) return R_NilValue; + if (TYPEOF(res) == PROMSXP) + res = internal::Rcpp_eval_impl(res, env); #else SEXP res = R_getVarEx(name, env, FALSE, R_NilValue); #endif - /* We need to evaluate if it is a promise */ - if( TYPEOF(res) == PROMSXP){ - res = internal::Rcpp_eval_impl( res, env ) ; - } return res ; } @@ -146,14 +144,12 @@ namespace Rcpp{ #if R_VERSION < R_Version(4,5,0) SEXP res = Rf_findVar( name, env ) ; if (res == R_UnboundValue) throw binding_not_found(name.c_str()); + if (TYPEOF(res) == PROMSXP) + res = internal::Rcpp_eval_impl(res, env); #else SEXP res = R_getVarEx(name, env, TRUE, R_NilValue); if (res == R_NilValue) throw binding_not_found(name.c_str()); #endif - /* We need to evaluate if it is a promise */ - if( TYPEOF(res) == PROMSXP){ - res = internal::Rcpp_eval_impl( res, env ) ; - } return res ; }