Skip to content

Commit 81c8339

Browse files
committed
shielding #789 behind a new #define to effectively disable it now
1 parent b52f690 commit 81c8339

File tree

10 files changed

+137
-50
lines changed

10 files changed

+137
-50
lines changed

ChangeLog

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
2018-01-15 Dirk Eddelbuettel <edd@debian.org>
2+
3+
* inst/include/RcppCommon.h: Check for new #define RCPP_USE_UNWIND_PROTECT
4+
and unset if defined, this is being be used to "park" code from #789
5+
* inst/include/Rcpp/Environment.h: Ifdef #789 via RCPP_USE_UNWIND_PROTECT
6+
* inst/include/Rcpp/Language.h: Idem
7+
* inst/include/Rcpp/api/meat/Rcpp_eval.h: Idem
8+
* inst/include/Rcpp/exceptions.h: Idem
9+
* inst/include/Rcpp/macros/macros.h: Idem
10+
11+
* inst/unitTests/runit.stack.R: Ensure test is not running
12+
113
2018-01-14 Dirk Eddelbuettel <edd@debian.org>
214

315
* DESCRIPTION (Version, Date): New minor version 0.12.14.8
@@ -50,32 +62,7 @@
5062

5163
* inst/include/Rcpp/api/meat/Rcpp_eval.h: Add Rcpp_fast_eval() for safe
5264
and fast evaluation of R code using the new protect-unwind API in R 3.5.
53-
Unlike Rcpp_eval(), this does not evaluate R code within tryCatch() in
54-
order to avoid the catching overhead. R longjumps are now correctly
55-
intercepted and rethrown. Following this change the C++ stack is now
56-
safely unwound when a longjump is detected while calling into R code.
57-
This includes the following cases: caught condition of any class, long
58-
return, restart jump, debugger exit.
59-
60-
Rcpp_eval() also uses the protect-unwind API in order to gain safety.
61-
To maintain compatibility it still catches errors and interrupts in
62-
order to rethrow them as typed C++ exceptions. If you don't need to
63-
catch those, consider using Rcpp_fast_eval() instead to avoid the
64-
overhead.
65-
66-
These improvements are only available for R 3.5.0 and greater. You also
67-
need to explicitly define `RCPP_PROTECTED_EVAL` before including Rcpp.h.
68-
When compiled with old versions of R, Rcpp_fast_eval() always falls back
69-
to Rcpp_eval(). This is in contrast to internal::Rcpp_eval_impl() which
70-
falls back to Rf_eval() and which is used in performance-sensititive
71-
places.
72-
73-
Note that with this change, Rcpp_eval() now behaves like the C function
74-
Rf_eval() whereas it used to behave like the R function base::eval().
75-
This has subtle implications for control flow. For instance evaluating a
76-
return() expression within a frame environment now returns from that
77-
frame rather than from the Rcpp_eval() call. The old semantics were a
78-
consequence of using evalq() internally and were not documented.
65+
[ This is however disabled for release 0.12.15. ]
7966

8067
* inst/include/Rcpp/exceptions.h: Add LongjumpException and
8168
resumeJump() to support Rcpp_fast_eval().

inst/NEWS.Rd

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
before including \code{Rcpp.h}). Longjumps of all kinds (condition
2020
catching, returns, restarts, debugger exit) are appropriately
2121
detected and handled, e.g. the C++ stack unwinds correctly
22-
(Lionel in \ghpr{789}).
22+
(Lionel in \ghpr{789}). [ Committed but subsequently disabled in release
23+
0.12.15 ]
2324
\item The new function \code{Rcpp_fast_eval()} can be used for
2425
performance-sensitive evaluation of R code. Unlike
2526
\code{Rcpp_eval()}, it does not try to catch errors with
@@ -29,7 +30,8 @@
2930
you are relying on error rethrowing, you have to use the slower
3031
\code{Rcpp_eval()}. On old R versions \code{Rcpp_fast_eval()}
3132
falls back to \code{Rcpp_eval()} so it is safe to use against any
32-
versions of R (Lionel in \ghpr{789}).
33+
versions of R (Lionel in \ghpr{789}). [ Committed but subsequently
34+
disabled in release 0.12.15 ]
3335
\item Overly-clever checks for \code{NA} have been removed (Kevin in
3436
\ghpr{790}).
3537
\item The included tinyformat has been updated to the current version,

inst/include/Rcpp/Environment.h

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ namespace Rcpp{
109109

110110
/* We need to evaluate if it is a promise */
111111
if( TYPEOF(res) == PROMSXP){
112-
res = internal::Rcpp_eval_impl( res, env ) ;
112+
#if defined(RCPP_USE_UNWIND_PROTECT)
113+
res = internal::Rcpp_eval_impl(res, env);
114+
#else
115+
res = Rf_eval(res, env);
116+
#endif
113117
}
114118
return res ;
115119
}
@@ -129,7 +133,11 @@ namespace Rcpp{
129133

130134
/* We need to evaluate if it is a promise */
131135
if( TYPEOF(res) == PROMSXP){
132-
res = internal::Rcpp_eval_impl( res, env ) ;
136+
#if defined(RCPP_USE_UNWIND_PROTECT)
137+
res = internal::Rcpp_eval_impl(res, env);
138+
#else
139+
res = Rf_eval(res, env);
140+
#endif
133141
}
134142
return res ;
135143
}
@@ -151,7 +159,11 @@ namespace Rcpp{
151159

152160
/* We need to evaluate if it is a promise */
153161
if( TYPEOF(res) == PROMSXP){
154-
res = internal::Rcpp_eval_impl( res, env ) ;
162+
#if defined(RCPP_USE_UNWIND_PROTECT)
163+
res = internal::Rcpp_eval_impl(res, env);
164+
#else
165+
res = Rf_eval(res, env);
166+
#endif
155167
}
156168
return res ;
157169
}
@@ -174,7 +186,11 @@ namespace Rcpp{
174186

175187
/* We need to evaluate if it is a promise */
176188
if( TYPEOF(res) == PROMSXP){
177-
res = internal::Rcpp_eval_impl( res, env ) ;
189+
#if defined(RCPP_USE_UNWIND_PROTECT)
190+
res = internal::Rcpp_eval_impl(res, env);
191+
#else
192+
res = Rf_eval(res, env);
193+
#endif
178194
}
179195
return res ;
180196
}

inst/include/Rcpp/Language.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,18 @@ namespace Rcpp{
145145
}
146146

147147
SEXP fast_eval() const {
148-
return internal::Rcpp_eval_impl( Storage::get__(), R_GlobalEnv) ;
148+
#if defined(RCPP_USE_UNWIND_PROTECT)
149+
return internal::Rcpp_eval_impl( Storage::get__(), R_GlobalEnv);
150+
#else
151+
return Rf_eval(Storage::get__(), R_GlobalEnv);
152+
#endif
149153
}
150154
SEXP fast_eval(SEXP env ) const {
155+
#if defined(RCPP_USE_UNWIND_PROTECT)
151156
return internal::Rcpp_eval_impl( Storage::get__(), env) ;
157+
#else
158+
return Rf_eval(Storage::get__(), env);
159+
#endif
152160
}
153161

154162
void update( SEXP x){

inst/include/Rcpp/api/meat/Rcpp_eval.h

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@
2121
#include <Rcpp/Interrupt.h>
2222
#include <Rversion.h>
2323

24-
#if (defined(RCPP_PROTECTED_EVAL) && defined(R_VERSION) && R_VERSION >= R_Version(3, 5, 0))
25-
#define RCPP_USE_PROTECT_UNWIND
24+
// outer definition from RcppCommon.h
25+
#if defined(RCPP_USE_UNWIND_PROTECT)
26+
#if (defined(RCPP_PROTECTED_EVAL) && defined(R_VERSION) && R_VERSION >= R_Version(3, 5, 0))
27+
// file-local and only used here
28+
#define RCPP_USE_PROTECT_UNWIND
29+
#endif
2630
#endif
2731

28-
2932
namespace Rcpp {
3033
namespace internal {
3134

@@ -96,39 +99,48 @@ inline SEXP Rcpp_eval(SEXP expr, SEXP env) {
9699

97100
// 'identity' function used to capture errors, interrupts
98101
SEXP identity = Rf_findFun(::Rf_install("identity"), R_BaseNamespace);
99-
102+
100103
if (identity == R_UnboundValue) {
101104
stop("Failed to find 'base::identity()'");
102105
}
103106

104107
// define the evalq call -- the actual R evaluation we want to execute
105108
Shield<SEXP> evalqCall(Rf_lang3(::Rf_install("evalq"), expr, env));
106-
109+
107110
// define the call -- enclose with `tryCatch` so we can record and forward error messages
108111
Shield<SEXP> call(Rf_lang4(::Rf_install("tryCatch"), evalqCall, identity, identity));
109112
SET_TAG(CDDR(call), ::Rf_install("error"));
110113
SET_TAG(CDDR(CDR(call)), ::Rf_install("interrupt"));
111114

115+
#if defined(RCPP_USE_UNWIND_PROTECT)
116+
Shield<SEXP> res(::Rf_eval(call, R_GlobalEnv)) // execute the call
117+
#else
112118
Shield<SEXP> res(internal::Rcpp_eval_impl(call, R_GlobalEnv));
119+
#endif
113120

114121
// check for condition results (errors, interrupts)
115122
if (Rf_inherits(res, "condition")) {
116-
123+
117124
if (Rf_inherits(res, "error")) {
118-
125+
119126
Shield<SEXP> conditionMessageCall(::Rf_lang2(::Rf_install("conditionMessage"), res));
120-
121-
Shield<SEXP> conditionMessage(internal::Rcpp_eval_impl(conditionMessageCall, R_GlobalEnv));
127+
128+
#if defined(RCPP_USE_UNWIND_PROTECT)
129+
Shield<SEXP> conditionMessage(internal::Rcpp_eval_impl(conditionMessageCall,
130+
R_GlobalEnv));
131+
#else
132+
Shield<SEXP> conditionMessage(::Rf_eval(conditionMessageCall, R_GlobalEnv));
133+
#endif
122134
throw eval_error(CHAR(STRING_ELT(conditionMessage, 0)));
123135
}
124-
136+
125137
// check for interrupt
126138
if (Rf_inherits(res, "interrupt")) {
127139
throw internal::InterruptedException();
128140
}
129-
141+
130142
}
131-
143+
132144
return res;
133145
}
134146

inst/include/Rcpp/exceptions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ namespace Rcpp {
111111
throw Rcpp::exception(message.c_str());
112112
} // #nocov end
113113

114+
#if defined(RCPP_USE_UNWIND_PROTECT)
114115
namespace internal {
115116

116117
struct LongjumpException {
@@ -126,6 +127,7 @@ namespace Rcpp {
126127
}
127128

128129
} // namespace internal
130+
#endif
129131

130132
} // namespace Rcpp
131133

inst/include/Rcpp/macros/macros.h

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
#endif
3737

3838
#ifndef VOID_END_RCPP
39-
#define VOID_END_RCPP \
39+
// longer form with Rcpp::internal::LongjumpException first, alternate below #else
40+
#if defined(RCPP_USE_UNWIND_PROTECT)
4041
} \
4142
catch( Rcpp::internal::InterruptedException &__ex__) { \
4243
rcpp_output_type = 1 ; \
@@ -66,13 +67,42 @@
6667
SEXP expr = PROTECT( Rf_lang2( stop_sym , rcpp_output_condition ) ) ; \
6768
Rf_eval( expr, R_GlobalEnv ) ; \
6869
}
70+
#else
71+
#define VOID_END_RCPP \
72+
} \
73+
catch( Rcpp::internal::InterruptedException &__ex__) { \
74+
rcpp_output_type = 1 ; \
75+
} \
76+
catch(Rcpp::exception& __ex__) { \
77+
rcpp_output_type = 2 ; \
78+
rcpp_output_condition = PROTECT(rcpp_exception_to_r_condition(__ex__)) ; \
79+
} \
80+
catch( std::exception& __ex__ ){ \
81+
rcpp_output_type = 2 ; \
82+
rcpp_output_condition = PROTECT(exception_to_r_condition(__ex__)) ; \
83+
} \
84+
catch( ... ){ \
85+
rcpp_output_type = 2 ; \
86+
rcpp_output_condition = PROTECT(string_to_try_error("c++ exception (unknown reason)")) ; \
87+
} \
88+
if( rcpp_output_type == 1 ){ \
89+
Rf_onintr() ; \
90+
} \
91+
if( rcpp_output_type == 2 ){ \
92+
SEXP stop_sym = Rf_install( "stop" ) ; \
93+
SEXP expr = PROTECT( Rf_lang2( stop_sym , rcpp_output_condition ) ) ; \
94+
Rf_eval( expr, R_GlobalEnv ) ; \
95+
}
96+
#endif
6997
#endif
7098

7199
#ifndef END_RCPP
72100
#define END_RCPP VOID_END_RCPP return R_NilValue;
73101
#endif
74102

75103
#ifndef END_RCPP_RETURN_ERROR
104+
// longer form with Rcpp::internal::LongjumpException first, alternate below #else
105+
#if defined(RCPP_USE_UNWIND_PROTECT)
76106
#define END_RCPP_RETURN_ERROR \
77107
} \
78108
catch (Rcpp::internal::InterruptedException &__ex__) { \
@@ -89,6 +119,20 @@
89119
return string_to_try_error("c++ exception (unknown reason)"); \
90120
} \
91121
return R_NilValue;
122+
#else
123+
#define END_RCPP_RETURN_ERROR \
124+
} \
125+
catch (Rcpp::internal::InterruptedException &__ex__) { \
126+
return Rcpp::internal::interruptedError(); \
127+
} \
128+
catch (std::exception &__ex__) { \
129+
return exception_to_try_error(__ex__); \
130+
} \
131+
catch (...) { \
132+
return string_to_try_error("c++ exception (unknown reason)"); \
133+
} \
134+
return R_NilValue;
135+
#endif
92136
#endif
93137

94138
#define Rcpp_error(MESSAGE) throw Rcpp::exception(MESSAGE, __FILE__, __LINE__)

inst/include/RcppCommon.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@
2626
// #define RCPP_DEBUG_LEVEL 1
2727
// #define RCPP_DEBUG_MODULE_LEVEL 1
2828

29+
// PR #798 by Lionel seems to have created some side-effects possibly related to
30+
// UnwinProtect is currently implement in R-devel. This #define needs to be set to
31+
// enable it, in most cases you want to be disabled.
32+
// #define RCPP_USE_UNWIND_PROTECT 1
33+
// so here _explicitly_ disable it for now
34+
#ifdef RCPP_USE_UNWIND_PROTECT
35+
#undef RCPP_USE_UNWIND_PROTECT
36+
#endif
37+
2938
#include <Rcpp/r/headers.h>
3039

3140
/**
@@ -74,9 +83,10 @@ namespace Rcpp {
7483

7584
namespace Rcpp {
7685

77-
SEXP Rcpp_fast_eval(SEXP expr_, SEXP env = R_GlobalEnv);
7886
SEXP Rcpp_eval(SEXP expr_, SEXP env = R_GlobalEnv);
7987

88+
// from PR#789
89+
SEXP Rcpp_fast_eval(SEXP expr_, SEXP env = R_GlobalEnv);
8090
namespace internal {
8191
SEXP Rcpp_eval_impl(SEXP expr, SEXP env = R_GlobalEnv);
8292
}

inst/unitTests/cpp/stack.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,6 @@ SEXP testFastEval(RObject expr, Environment env, LogicalVector indicator) {
4848

4949
// [[Rcpp::export]]
5050
SEXP testSendInterrupt() {
51-
Rf_onintr();
52-
return R_NilValue;
51+
Rf_onintr();
52+
return R_NilValue;
5353
}

inst/unitTests/runit.stack.R

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@
1818
# along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
1919

2020
.runThisTest <- Sys.getenv("RunAllRcppTests") == "yes"
21+
.onLinux <- .Platform$OS.type == "unix" && unname(Sys.info()["sysname"]) == "Linux"
2122

23+
## As of release 0.12.15, the stack unwinding is experimental and not used
24+
## See the #define in RcppCommon.h to change it
2225

23-
if (FALSE && .runThisTest) {
26+
.runThisTest <- FALSE
27+
28+
29+
if (.runThisTest) {
2430

2531
.setUp <- Rcpp:::unitTestSetup("stack.cpp")
2632

0 commit comments

Comments
 (0)