extendr_ffi/backports.rs
1//! Backport functions
2//!
3//! R's C API is changing and is stabilizing. As such some functions
4//! that were available in previous versions of R are not available
5//! in later versions of R, or they cause a warning in `R CMD check`.
6//!
7//! Use the functions in this module to ensure backwards compatibility.
8
9// R 4.5 backports notes saved in wayback machine here:
10// https://web.archive.org/web/20250325171443/https://rstudio.github.io/r-manuals/r-exts/The-R-API.html#moving-into-c-api-compliance
11
12use crate::{Rboolean, SEXP};
13
14#[cfg(r_4_5)]
15use crate::{R_NilValue, Rf_setAttrib, CAR, CDR, TAG};
16
17#[cfg(not(r_4_5))]
18use crate::R_UnboundValue;
19
20extern "C" {
21 #[cfg(not(r_4_5))]
22 fn ENCLOS(x: SEXP) -> SEXP;
23
24 #[cfg(r_4_5)]
25 fn R_ParentEnv(x: SEXP) -> SEXP;
26
27 #[cfg(not(r_4_5))]
28 fn Rf_findVar(arg1: SEXP, arg2: SEXP) -> SEXP;
29
30 #[cfg(r_4_5)]
31 fn R_getVar(sym: SEXP, rho: SEXP, inherits: Rboolean) -> SEXP;
32
33 #[cfg(not(r_4_5))]
34 fn Rf_findVarInFrame(arg1: SEXP, arg2: SEXP) -> SEXP;
35
36 #[cfg(r_4_5)]
37 fn R_getVarEx(sym: SEXP, rho: SEXP, inherits: Rboolean, ifnotfound: SEXP) -> SEXP;
38
39 #[cfg(not(r_4_5))]
40 fn CLOENV(x: SEXP) -> SEXP;
41
42 #[cfg(r_4_5)]
43 fn R_ClosureEnv(x: SEXP) -> SEXP;
44
45 #[cfg(not(r_4_5))]
46 fn BODY(x: SEXP) -> SEXP;
47
48 #[cfg(r_4_5)]
49 fn R_ClosureBody(x: SEXP) -> SEXP;
50
51 #[cfg(not(r_4_5))]
52 fn FORMALS(x: SEXP) -> SEXP;
53
54 #[cfg(r_4_5)]
55 fn R_ClosureFormals(x: SEXP) -> SEXP;
56
57 #[cfg(not(r_4_5))]
58 fn DATAPTR(x: SEXP) -> *mut ::std::os::raw::c_void;
59
60 #[cfg(r_4_5)]
61 fn DATAPTR_RO(x: SEXP) -> *const ::std::os::raw::c_void;
62
63 #[cfg(not(r_4_5))]
64 fn Rf_isFrame(x: SEXP) -> Rboolean;
65
66 #[cfg(r_4_5)]
67 fn Rf_isDataFrame(x: SEXP) -> Rboolean;
68
69 #[cfg(not(r_4_5))]
70 fn SET_ATTRIB(x: SEXP, v: SEXP);
71
72 #[cfg(r_4_5)]
73 fn CLEAR_ATTRIB(x: SEXP);
74}
75
76/// Returns the enclosing environment of env, which will usually be of type ENVSXP, except for the special environment R_EmptyEnv, which terminates the environment chain; its enclosing environment is R_NilValue.
77///
78/// # Safety
79///
80/// This function dereferences a raw SEXP pointer.
81/// The caller must ensure that `x` is a valid SEXP pointer to an environment.
82#[inline]
83pub unsafe fn get_parent_env(x: SEXP) -> SEXP {
84 #[cfg(not(r_4_5))]
85 {
86 ENCLOS(x)
87 }
88 #[cfg(r_4_5)]
89 {
90 R_ParentEnv(x)
91 }
92}
93
94/// Returns a variable from an environment
95///
96/// # Safety
97///
98/// This function dereferences raw SEXP pointers.
99/// The caller must ensure that `symbol` and `env` are valid SEXP pointers.
100#[inline]
101pub unsafe fn get_var(symbol: SEXP, env: SEXP) -> SEXP {
102 #[cfg(not(r_4_5))]
103 {
104 Rf_findVar(symbol, env)
105 }
106 #[cfg(r_4_5)]
107 {
108 R_getVar(symbol, env, Rboolean::TRUE)
109 }
110}
111
112/// Returns a variable from an environment, or `None` if unbound.
113///
114/// On R < 4.5, uses `Rf_findVar` and checks against `R_UnboundValue`.
115/// On R >= 4.5, uses `R_getVar` which signals an error if not found;
116/// callers should wrap this with `catch_r_error` if needed.
117///
118/// # Safety
119///
120/// This function dereferences raw SEXP pointers.
121/// The caller must ensure that `symbol` and `env` are valid SEXP pointers.
122#[inline]
123pub unsafe fn get_var_safe(symbol: SEXP, env: SEXP) -> Option<SEXP> {
124 #[cfg(not(r_4_5))]
125 {
126 let var = Rf_findVar(symbol, env);
127 if var == R_UnboundValue {
128 None
129 } else {
130 Some(var)
131 }
132 }
133 #[cfg(r_4_5)]
134 {
135 Some(R_getVar(symbol, env, Rboolean::TRUE))
136 }
137}
138
139/// Returns the value of the requested variable in an environment
140///
141/// # Safety
142///
143/// This function dereferences raw SEXP pointers.
144/// The caller must ensure that `env` and `symbol` are valid SEXP pointers.
145#[inline]
146pub unsafe fn get_var_in_frame(env: SEXP, symbol: SEXP) -> SEXP {
147 #[cfg(not(r_4_5))]
148 {
149 Rf_findVarInFrame(env, symbol)
150 }
151 #[cfg(r_4_5)]
152 {
153 R_getVarEx(symbol, env, Rboolean::FALSE, R_NilValue)
154 }
155}
156
157/// Return the environment of a closure
158///
159/// # Safety
160///
161/// This function dereferences a raw SEXP pointer.
162/// The caller must ensure that `x` is a valid SEXP pointer to a closure.
163#[inline]
164pub unsafe fn get_closure_env(x: SEXP) -> SEXP {
165 #[cfg(not(r_4_5))]
166 {
167 CLOENV(x)
168 }
169 #[cfg(r_4_5)]
170 {
171 R_ClosureEnv(x)
172 }
173}
174
175/// Return the body of a closure
176///
177/// # Safety
178///
179/// This function dereferences a raw SEXP pointer.
180/// The caller must ensure that `x` is a valid SEXP pointer to a closure.
181#[inline]
182pub unsafe fn get_closure_body(x: SEXP) -> SEXP {
183 #[cfg(not(r_4_5))]
184 {
185 BODY(x)
186 }
187 #[cfg(r_4_5)]
188 {
189 R_ClosureBody(x)
190 }
191}
192
193/// Access a closure's arguments
194///
195/// # Safety
196///
197/// This function dereferences a raw SEXP pointer.
198/// The caller must ensure that `x` is a valid SEXP pointer to a closure.
199#[inline]
200pub unsafe fn get_closure_formals(x: SEXP) -> SEXP {
201 #[cfg(not(r_4_5))]
202 {
203 FORMALS(x)
204 }
205 #[cfg(r_4_5)]
206 {
207 R_ClosureFormals(x)
208 }
209}
210
211/// Access a DATAPTR
212///
213/// # Safety
214///
215/// This function dereferences a raw SEXP pointer.
216/// The caller must ensure that `x` is a valid SEXP pointer.
217#[inline]
218pub unsafe fn dataptr(x: SEXP) -> *const ::std::os::raw::c_void {
219 #[cfg(not(r_4_5))]
220 {
221 DATAPTR(x) as *const _
222 }
223 #[cfg(r_4_5)]
224 {
225 DATAPTR_RO(x)
226 }
227}
228
229/// Check is data.frame
230///
231/// # Safety
232///
233/// This function dereferences a raw SEXP pointer.
234/// The caller must ensure that `x` is a valid SEXP pointer.
235#[inline]
236pub unsafe fn is_data_frame(x: SEXP) -> Rboolean {
237 #[cfg(not(r_4_5))]
238 {
239 Rf_isFrame(x)
240 }
241 #[cfg(r_4_5)]
242 {
243 Rf_isDataFrame(x)
244 }
245}
246
247/// Replace all attributes of `x` with the pairlist `attributes`
248///
249/// On R >= 4.5, where `SET_ATTRIB` is no longer part of the API, this clears
250/// the attributes with `CLEAR_ATTRIB` and sets each one with `Rf_setAttrib`.
251///
252/// # Safety
253///
254/// This function dereferences raw SEXP pointers.
255/// The caller must ensure that `x` is a valid SEXP pointer and that `attributes`
256/// is a valid pairlist or `R_NilValue`, and that both are protected.
257#[inline]
258pub unsafe fn set_attrib(x: SEXP, attributes: SEXP) {
259 #[cfg(not(r_4_5))]
260 {
261 SET_ATTRIB(x, attributes)
262 }
263 #[cfg(r_4_5)]
264 {
265 CLEAR_ATTRIB(x);
266 let mut node = attributes;
267 while node != R_NilValue {
268 Rf_setAttrib(x, TAG(node), CAR(node));
269 node = CDR(node);
270 }
271 }
272}