Skip to content
8 changes: 8 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
2026-03-29 Iñaki Ucar <iucar@fedoraproject.org>

* 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 Iñaki Ucar <iucar@fedoraproject.org>

* inst/include/Rcpp/internal/r_vector.h: Use dataptr() again to avoid an
Expand Down
69 changes: 18 additions & 51 deletions inst/include/Rcpp/Environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down Expand Up @@ -95,20 +96,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);
}

/**
Expand All @@ -122,16 +111,12 @@ 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;
if (TYPEOF(res) == PROMSXP)
res = internal::Rcpp_eval_impl(res, env);
#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 ) ;
}
return res ;
}

Expand All @@ -144,21 +129,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);
}

/**
Expand All @@ -171,19 +143,13 @@ 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());
if (TYPEOF(res) == PROMSXP)
res = internal::Rcpp_eval_impl(res, env);
#else
SEXP res = R_getVarEx(name, env, TRUE, R_UnboundValue);
SEXP res = R_getVarEx(name, env, TRUE, NULL);
if (res == NULL) 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 ) ;
}
return res ;
}

Expand All @@ -199,10 +165,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, NULL);
return res != NULL;
#endif
return res != R_UnboundValue ;
}

/**
Expand Down
12 changes: 7 additions & 5 deletions inst/include/Rcpp/Function.h
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down Expand Up @@ -71,12 +72,13 @@ namespace Rcpp{
Function_Impl(const std::string& name, const std::string& ns) {
#if R_VERSION < R_Version(4,5,0)
Shield<SEXP> env(Rf_findVarInFrame(R_NamespaceRegistry, Rf_install(ns.c_str())));
if (env == R_UnboundValue)
stop("there is no namespace called \"%s\"", ns);
#else
Shield<SEXP> env(R_getVarEx(Rf_install(ns.c_str()), R_NamespaceRegistry, FALSE, R_UnboundValue));
#endif
if (env == R_UnboundValue) {
Shield<SEXP> env(R_getVarEx(Rf_install(ns.c_str()), R_NamespaceRegistry, FALSE, R_NilValue));
if (env == R_NilValue)
stop("there is no namespace called \"%s\"", ns);
}
#endif
get_function(name, env);
}

Expand Down
14 changes: 10 additions & 4 deletions inst/include/Rcpp/Promise.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down Expand Up @@ -48,13 +49,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
}

/**
Expand Down
6 changes: 1 addition & 5 deletions inst/include/Rcpp/api/meat/Rcpp_eval.h
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down Expand Up @@ -56,10 +56,6 @@ inline SEXP Rcpp_eval(SEXP expr, SEXP env) {
// 'identity' function used to capture errors, interrupts
Shield<SEXP> 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<SEXP> evalqCall(Rf_lang3(::Rf_install("evalq"), expr, env));

Expand Down
Loading