Skip to main content

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}