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(not(r_4_5))]
15use crate::R_UnboundValue;
16
17extern "C" {
18 #[cfg(not(r_4_5))]
19 fn ENCLOS(x: SEXP) -> SEXP;
20
21 #[cfg(r_4_5)]
22 fn R_ParentEnv(x: SEXP) -> SEXP;
23
24 #[cfg(not(r_4_5))]
25 fn Rf_findVar(arg1: SEXP, arg2: SEXP) -> SEXP;
26
27 #[cfg(r_4_5)]
28 fn R_getVar(arg1: SEXP, arg2: SEXP) -> SEXP;
29
30 #[cfg(not(r_4_5))]
31 fn Rf_findVarInFrame(arg1: SEXP, arg2: SEXP) -> SEXP;
32
33 #[cfg(r_4_5)]
34 fn R_getVarEx(arg1: SEXP, arg2: SEXP) -> SEXP;
35
36 #[cfg(not(r_4_5))]
37 fn CLOENV(x: SEXP) -> SEXP;
38
39 #[cfg(r_4_5)]
40 fn R_ClosureEnv(x: SEXP) -> SEXP;
41
42 #[cfg(not(r_4_5))]
43 fn BODY(x: SEXP) -> SEXP;
44
45 #[cfg(r_4_5)]
46 fn R_ClosureBody(x: SEXP) -> SEXP;
47
48 #[cfg(not(r_4_5))]
49 fn FORMALS(x: SEXP) -> SEXP;
50
51 #[cfg(r_4_5)]
52 fn R_ClosureFormals(x: SEXP) -> SEXP;
53
54 #[cfg(not(r_4_5))]
55 fn DATAPTR(x: SEXP) -> *mut ::std::os::raw::c_void;
56
57 #[cfg(r_4_5)]
58 fn DATAPTR_RO(x: SEXP) -> *const ::std::os::raw::c_void;
59
60 #[cfg(not(r_4_5))]
61 fn Rf_isFrame(x: SEXP) -> Rboolean;
62
63 #[cfg(r_4_5)]
64 fn Rf_isDataFrame(x: SEXP) -> Rboolean;
65}
66
67/// 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.
68///
69/// # Safety
70///
71/// This function dereferences a raw SEXP pointer.
72/// The caller must ensure that `x` is a valid SEXP pointer to an environment.
73#[inline]
74pub unsafe fn get_parent_env(x: SEXP) -> SEXP {
75 #[cfg(not(r_4_5))]
76 {
77 ENCLOS(x)
78 }
79 #[cfg(r_4_5)]
80 {
81 R_ParentEnv(x)
82 }
83}
84
85/// Returns a variable from an environment
86///
87/// # Safety
88///
89/// This function dereferences raw SEXP pointers.
90/// The caller must ensure that `symbol` and `env` are valid SEXP pointers.
91#[inline]
92pub unsafe fn get_var(symbol: SEXP, env: SEXP) -> SEXP {
93 #[cfg(not(r_4_5))]
94 {
95 Rf_findVar(symbol, env)
96 }
97 #[cfg(r_4_5)]
98 {
99 R_getVar(symbol, env)
100 }
101}
102
103/// Returns a variable from an environment, or `None` if unbound.
104///
105/// On R < 4.5, uses `Rf_findVar` and checks against `R_UnboundValue`.
106/// On R >= 4.5, uses `R_getVar` which signals an error if not found;
107/// callers should wrap this with `catch_r_error` if needed.
108///
109/// # Safety
110///
111/// This function dereferences raw SEXP pointers.
112/// The caller must ensure that `symbol` and `env` are valid SEXP pointers.
113#[inline]
114pub unsafe fn get_var_safe(symbol: SEXP, env: SEXP) -> Option<SEXP> {
115 #[cfg(not(r_4_5))]
116 {
117 let var = Rf_findVar(symbol, env);
118 if var == R_UnboundValue {
119 None
120 } else {
121 Some(var)
122 }
123 }
124 #[cfg(r_4_5)]
125 {
126 Some(R_getVar(symbol, env))
127 }
128}
129
130/// Returns the value of the requested variable in an environment
131///
132/// # Safety
133///
134/// This function dereferences raw SEXP pointers.
135/// The caller must ensure that `symbol` and `env` are valid SEXP pointers.
136#[inline]
137pub unsafe fn get_var_in_frame(symbol: SEXP, env: SEXP) -> SEXP {
138 #[cfg(not(r_4_5))]
139 {
140 Rf_findVarInFrame(symbol, env)
141 }
142 #[cfg(r_4_5)]
143 {
144 R_getVarEx(env, symbol)
145 }
146}
147
148/// Return the environment of a closure
149///
150/// # Safety
151///
152/// This function dereferences a raw SEXP pointer.
153/// The caller must ensure that `x` is a valid SEXP pointer to a closure.
154#[inline]
155pub unsafe fn get_closure_env(x: SEXP) -> SEXP {
156 #[cfg(not(r_4_5))]
157 {
158 CLOENV(x)
159 }
160 #[cfg(r_4_5)]
161 {
162 R_ClosureEnv(x)
163 }
164}
165
166/// Return the body of a closure
167///
168/// # Safety
169///
170/// This function dereferences a raw SEXP pointer.
171/// The caller must ensure that `x` is a valid SEXP pointer to a closure.
172#[inline]
173pub unsafe fn get_closure_body(x: SEXP) -> SEXP {
174 #[cfg(not(r_4_5))]
175 {
176 BODY(x)
177 }
178 #[cfg(r_4_5)]
179 {
180 R_ClosureBody(x)
181 }
182}
183
184/// Access a closure's arguments
185///
186/// # Safety
187///
188/// This function dereferences a raw SEXP pointer.
189/// The caller must ensure that `x` is a valid SEXP pointer to a closure.
190#[inline]
191pub unsafe fn get_closure_formals(x: SEXP) -> SEXP {
192 #[cfg(not(r_4_5))]
193 {
194 FORMALS(x)
195 }
196 #[cfg(r_4_5)]
197 {
198 R_ClosureFormals(x)
199 }
200}
201
202/// Access a DATAPTR
203///
204/// # Safety
205///
206/// This function dereferences a raw SEXP pointer.
207/// The caller must ensure that `x` is a valid SEXP pointer.
208#[inline]
209pub unsafe fn dataptr(x: SEXP) -> *const ::std::os::raw::c_void {
210 #[cfg(not(r_4_5))]
211 {
212 DATAPTR(x) as *const _
213 }
214 #[cfg(r_4_5)]
215 {
216 DATAPTR_RO(x)
217 }
218}
219
220/// Check is data.frame
221///
222/// # Safety
223///
224/// This function dereferences a raw SEXP pointer.
225/// The caller must ensure that `x` is a valid SEXP pointer.
226#[inline]
227pub unsafe fn is_data_frame(x: SEXP) -> Rboolean {
228 #[cfg(not(r_4_5))]
229 {
230 Rf_isFrame(x)
231 }
232 #[cfg(r_4_5)]
233 {
234 Rf_isDataFrame(x)
235 }
236}