Skip to main content

miniextendr_api/
expression.rs

1//! Safe wrappers for R expression evaluation.
2//!
3//! This module provides ergonomic types for building and evaluating R function
4//! calls from Rust, handling GC protection and error propagation automatically.
5//!
6//! # Types
7//!
8//! | Type | Purpose |
9//! |------|---------|
10//! | [`RSymbol`] | Interned R symbol (SYMSXP) |
11//! | [`RCall`] | Builder for R function calls (LANGSXP) |
12//! | [`REnv`] | Well-known R environments |
13//! | [`REvalError`] | An R error caught by [`RCall::eval`] / [`r_eval_str`] / [`try_eval_with_handlers`] |
14//!
15//! # Example
16//!
17//! ```ignore
18//! use miniextendr_api::expression::{RCall, REnv};
19//!
20//! unsafe {
21//!     // Call paste0("hello", " world") in the base environment
22//!     let result = RCall::new("paste0")
23//!         .arg(Rf_mkString(c"hello".as_ptr()))
24//!         .arg(Rf_mkString(c" world".as_ptr()))
25//!         .eval(REnv::base().as_sexp())?;
26//! }
27//! ```
28
29use crate::condition::ConditionClass;
30use crate::from_r::TryFromSexp;
31use crate::gc_protect::{OwnedProtect, ProtectScope};
32use crate::into_r::IntoR;
33use crate::protect_pool::ProtectPool;
34use crate::sexp_ext::PairListExt;
35use crate::sys::{
36    self, ParseStatus, R_BaseEnv, R_EmptyEnv, R_GlobalEnv, R_ParseVector, Rf_install,
37};
38use crate::{SEXP, SexpExt};
39use std::ffi::{CStr, CString};
40use std::os::raw::c_void;
41
42// region: RSymbol
43
44/// A safe wrapper around R symbols (SYMSXP).
45///
46/// R symbols are interned strings used as variable and function names.
47/// They are never garbage collected, so `RSymbol` does not need GC protection.
48///
49/// # Example
50///
51/// ```ignore
52/// let sym = RSymbol::new("paste0");
53/// // sym.as_sexp() is a SYMSXP that can be used in call construction
54/// ```
55pub struct RSymbol {
56    sexp: SEXP,
57}
58
59impl RSymbol {
60    /// Create or retrieve an interned R symbol.
61    ///
62    /// # Safety
63    ///
64    /// Must be called from the R main thread.
65    ///
66    /// # Panics
67    ///
68    /// Panics if `name` contains a null byte.
69    #[inline]
70    pub unsafe fn new(name: &str) -> Self {
71        let c_name = CString::new(name).expect("symbol name must not contain null bytes");
72        RSymbol {
73            sexp: unsafe { Rf_install(c_name.as_ptr()) },
74        }
75    }
76
77    /// Create a symbol from a C string literal.
78    ///
79    /// This avoids the allocation needed by [`new`](Self::new) when you have
80    /// a `&CStr` available (e.g., from `c"name"` literals).
81    ///
82    /// # Safety
83    ///
84    /// Must be called from the R main thread.
85    #[inline]
86    pub unsafe fn from_cstr(name: &CStr) -> Self {
87        RSymbol {
88            sexp: unsafe { Rf_install(name.as_ptr()) },
89        }
90    }
91
92    /// Get the underlying SEXP.
93    #[inline]
94    pub fn as_sexp(&self) -> SEXP {
95        self.sexp
96    }
97}
98// endregion
99
100// region: REnv
101
102/// Handle to a well-known R environment.
103///
104/// Provides access to R's standard environments without raw FFI calls.
105pub struct REnv {
106    sexp: SEXP,
107}
108
109impl REnv {
110    /// The global environment (`R_GlobalEnv`).
111    ///
112    /// # Safety
113    ///
114    /// Must be called from the R main thread.
115    #[inline]
116    pub unsafe fn global() -> Self {
117        REnv {
118            sexp: unsafe { R_GlobalEnv },
119        }
120    }
121
122    /// The base environment (`R_BaseEnv`).
123    ///
124    /// # Safety
125    ///
126    /// Must be called from the R main thread.
127    #[inline]
128    pub unsafe fn base() -> Self {
129        REnv {
130            sexp: unsafe { R_BaseEnv },
131        }
132    }
133
134    /// The empty environment (`R_EmptyEnv`).
135    ///
136    /// # Safety
137    ///
138    /// Must be called from the R main thread.
139    #[inline]
140    pub unsafe fn empty() -> Self {
141        REnv {
142            sexp: unsafe { R_EmptyEnv },
143        }
144    }
145
146    /// The base namespace (`SEXP::base_namespace()`).
147    ///
148    /// Unlike [`base()`](Self::base) which is the base *environment* (exported
149    /// functions visible to users), this is the base *namespace* (includes
150    /// internal helpers). Rarely needed — prefer [`base()`](Self::base) unless
151    /// you specifically need unexported base internals.
152    ///
153    /// # Safety
154    ///
155    /// Must be called from the R main thread.
156    #[inline]
157    pub fn base_namespace() -> Self {
158        REnv {
159            sexp: SEXP::base_namespace(),
160        }
161    }
162
163    /// A package's namespace environment.
164    ///
165    /// Finds the namespace for a loaded package. Use this to evaluate functions
166    /// that live in a specific package (e.g., `slot()` from `methods`).
167    ///
168    /// This is a safe wrapper around `getNamespace(name)`, evaluated through
169    /// [`RCall::eval`], so a missing namespace returns `Err` instead of
170    /// longjmping through Rust frames.
171    ///
172    /// # Safety
173    ///
174    /// Must be called from the R main thread.
175    ///
176    /// # Errors
177    ///
178    /// R's error when the namespace cannot be loaded (a
179    /// `packageNotFoundError` for a package that is not installed).
180    pub unsafe fn package_namespace(name: &str) -> Result<Self, REvalError> {
181        unsafe {
182            let name_sexp = OwnedProtect::new(SEXP::scalar_string_from_str(name));
183            RCall::new("getNamespace")
184                .arg(name_sexp.get())
185                .eval(R_BaseEnv)
186                .map(|sexp| REnv { sexp })
187        }
188    }
189
190    /// The current execution environment.
191    ///
192    /// Returns the environment of the innermost active closure on R's call
193    /// stack, or the global environment if no closure is active.
194    ///
195    /// Useful when you need to evaluate an expression in the caller's context
196    /// rather than a fixed well-known environment.
197    ///
198    /// # Safety
199    ///
200    /// Must be called from the R main thread.
201    #[inline]
202    pub unsafe fn caller() -> Self {
203        REnv {
204            sexp: unsafe { sys::R_GetCurrentEnv() },
205        }
206    }
207
208    /// Wrap an arbitrary environment SEXP.
209    ///
210    /// # Safety
211    ///
212    /// `sexp` must be a valid ENVSXP.
213    #[inline]
214    pub unsafe fn from_sexp(sexp: SEXP) -> Self {
215        REnv { sexp }
216    }
217
218    /// Get the underlying SEXP.
219    #[inline]
220    pub fn as_sexp(&self) -> SEXP {
221        self.sexp
222    }
223
224    /// The value bound to `name` in this environment's own frame, as R's
225    /// `get0(name, envir = env, inherits = FALSE)` reads it, or `None` when
226    /// the frame has no such binding. The enclosing environments are not
227    /// searched; [`get_inherits`](Self::get_inherits) searches them.
228    ///
229    /// A binding to `NULL` is `Some` of `NULL`. Reading a binding does what
230    /// `get0()` does:
231    ///
232    /// - A promise is forced, once, and its value is kept in the promise. A
233    ///   function in a lazy-loaded namespace, a `delayedAssign()` and an
234    ///   argument of an R function are promises. So this is not a check for
235    ///   whether a name is bound without running anything.
236    /// - An active binding (`makeActiveBinding()`) calls its function.
237    /// - A binding to the missing argument raises R's error.
238    ///
239    /// R 4.5 and later read the binding with `R_getVarEx`; R 4.4 evaluates
240    /// `base::get0()`. Both give the same results. An R error raised while
241    /// reading, such as a promise or an active binding that fails, unwinds the
242    /// Rust frames up to the miniextendr boundary, which hands it back to R,
243    /// as under [`eval_with_handlers`].
244    ///
245    /// # Safety
246    ///
247    /// R's main thread, inside a miniextendr boundary: a `#[miniextendr]`
248    /// function body, a [`with_r_unwind_protect`](crate::unwind_protect::with_r_unwind_protect)
249    /// closure, an ALTREP callback with the `r_unwind` or `rust_unwind` guard,
250    /// a connection callback, a [`with_r_thread`](crate::worker::with_r_thread)
251    /// closure.
252    ///
253    /// # Returns
254    ///
255    /// The value, **unprotected**. A plain binding, or a forced promise,
256    /// keeps it reachable while the name stays bound to it. An active
257    /// binding's value is new and held by nothing. Protect the value
258    /// ([`OwnedProtect`], [`Preserved`](crate::Preserved)) before the next
259    /// allocation unless the binding is known to be a plain value, and before
260    /// anything that may rebind the name: R code you evaluate, or
261    /// [`define`](Self::define).
262    ///
263    /// # Panics
264    ///
265    /// Panics if `name` contains a null byte.
266    pub unsafe fn get(&self, name: &str) -> Option<SEXP> {
267        unsafe { self.lookup(name, false) }
268    }
269
270    /// The value bound to `name` in this environment or the nearest enclosing
271    /// environment that binds it, as R's `get0(name, envir = env,
272    /// inherits = TRUE)` reads it, or `None` when none does.
273    ///
274    /// Bindings are read as [`get`](Self::get) reads them: a promise is
275    /// forced, an active binding is called. The search stops at the first
276    /// binding of `name`, whatever its value.
277    ///
278    /// # Safety
279    ///
280    /// As [`get`](Self::get).
281    ///
282    /// # Returns
283    ///
284    /// The value, **unprotected**, as [`get`](Self::get) returns it.
285    ///
286    /// # Panics
287    ///
288    /// Panics if `name` contains a null byte.
289    pub unsafe fn get_inherits(&self, name: &str) -> Option<SEXP> {
290        unsafe { self.lookup(name, true) }
291    }
292
293    /// Bind `name` to `value` in this environment's own frame, as
294    /// `assign(name, value, envir = env)` does: a binding of `name` in the
295    /// frame is replaced, and the enclosing environments are not touched.
296    ///
297    /// R raises its error, which unwinds as under [`get`](Self::get), when
298    /// the binding cannot be made: the empty environment, a locked
299    /// environment that lacks the binding (a package namespace, once the
300    /// package is loaded), or a locked binding. Assigning to an active binding
301    /// calls its function with `value`.
302    ///
303    /// # Safety
304    ///
305    /// As [`get`](Self::get). `value` is a valid SEXP; it needs no root of
306    /// its own, and the binding keeps it reachable afterwards.
307    ///
308    /// # Panics
309    ///
310    /// Panics if `name` contains a null byte.
311    pub unsafe fn define(&self, name: &str, value: SEXP) {
312        if !!name.contains('\0') {
    {
        ::core::panicking::panic_fmt(format_args!("a binding name must not contain null bytes"));
    }
};assert!(
313            !name.contains('\0'),
314            "a binding name must not contain null bytes"
315        );
316        unsafe { self.define_symbol(SEXP::symbol(name), value) }
317    }
318
319    /// [`define`](Self::define) for a name already interned as a symbol.
320    ///
321    /// # Safety
322    ///
323    /// As [`define`](Self::define); `symbol` is a SYMSXP.
324    pub(crate) unsafe fn define_symbol(&self, symbol: SEXP, value: SEXP) {
325        struct Define {
326            symbol: SEXP,
327            value: SEXP,
328            env: SEXP,
329        }
330
331        unsafe extern "C-unwind" fn define(data: *mut c_void) -> SEXP {
332            let data = unsafe { &*data.cast::<Define>() };
333            unsafe { sys::Rf_defineVar(data.symbol, data.value, data.env) };
334            SEXP::nil()
335        }
336
337        unsafe {
338            // Rooted ahead of the evaluation's own protect frame, so an R exit
339            // leaves the protect stack as the scope expects.
340            let scope = ProtectScope::new();
341            let env = scope.protect_raw(self.sexp);
342            let value = scope.protect_raw(value);
343            let mut data = Define { symbol, value, env };
344            crate::unwind_protect::call_unwinding(define, (&raw mut data).cast());
345        }
346    }
347
348    /// [`get`](Self::get) and [`get_inherits`](Self::get_inherits).
349    ///
350    /// # Safety
351    ///
352    /// As [`get`](Self::get).
353    unsafe fn lookup(&self, name: &str, inherits: bool) -> Option<SEXP> {
354        if !!name.contains('\0') {
    {
        ::core::panicking::panic_fmt(format_args!("a binding name must not contain null bytes"));
    }
};assert!(
355            !name.contains('\0'),
356            "a binding name must not contain null bytes"
357        );
358
359        // Each branch roots the environment ahead of the evaluation's own
360        // protect frame, so an R exit leaves the protect stack as the guards
361        // expect.
362        #[cfg(r_4_5)]
363        unsafe {
364            struct Lookup {
365                symbol: SEXP,
366                env: SEXP,
367                inherits: crate::Rboolean,
368            }
369
370            unsafe extern "C-unwind" fn lookup(data: *mut c_void) -> SEXP {
371                let data = unsafe { &*data.cast::<Lookup>() };
372                unsafe {
373                    sys::R_getVarEx(data.symbol, data.env, data.inherits, sys::R_UnboundValue)
374                }
375            }
376
377            let env = OwnedProtect::new(self.sexp);
378            let mut data = Lookup {
379                symbol: SEXP::symbol(name),
380                env: env.get(),
381                inherits: crate::Rboolean::from(inherits),
382            };
383            let value = crate::unwind_protect::call_unwinding(lookup, (&raw mut data).cast());
384            (value != sys::R_UnboundValue).then_some(value)
385        }
386
387        #[cfg(not(r_4_5))]
388        unsafe {
389            let env = OwnedProtect::new(self.sexp);
390            // A new object, so no binding holds it: `get0()` returns it only
391            // for an unbound name, which tells an unbound name from one bound
392            // to `NULL`.
393            let unbound = OwnedProtect::new(SEXP::alloc_list(0));
394            // Evaluated in the base environment, so `get0` is base's own.
395            let value = RCall::new("get0")
396                .arg_value(name)
397                .named_arg("envir", env.get())
398                .named_value("mode", "any")
399                .named_value("inherits", inherits)
400                .named_arg("ifnotfound", unbound.get())
401                .eval_with_handlers(R_BaseEnv);
402            (value != unbound.get()).then_some(value)
403        }
404    }
405}
406// endregion
407
408// region: RCall
409
410/// Builder for constructing and evaluating R function calls.
411///
412/// `RCall` constructs a LANGSXP (R language object) from a function name or
413/// SEXP and a sequence of arguments (optionally named). It handles GC
414/// protection during construction and evaluation.
415///
416/// # Example
417///
418/// ```ignore
419/// use miniextendr_api::expression::RCall;
420/// use miniextendr_api::sys;
421///
422/// unsafe {
423///     // seq_len(10)
424///     let result = RCall::new("seq_len")
425///         .arg(SEXP::scalar_integer(10))
426///         .eval_base()?;
427///
428///     // paste(x, collapse = ", ")
429///     let result = RCall::new("paste")
430///         .arg(some_sexp)
431///         .named_arg("collapse", sys::Rf_mkString(c", ".as_ptr()))
432///         .eval_base()?;
433/// }
434/// ```
435pub struct RCall {
436    /// Function symbol or SEXP.
437    fun: SEXP,
438    /// Arguments as (optional_name, value) pairs.
439    args: Vec<(Option<CString>, SEXP)>,
440    /// Roots the callable and every argument for the builder's lifetime,
441    /// except symbols, which need no root ([`needs_root`]).
442    ///
443    /// Besides keeping inline allocations alive, `ProtectPool` makes `RCall`
444    /// `!Send + !Sync`: construction, mutation, and drop stay on R's thread.
445    roots: ProtectPool,
446}
447
448impl RCall {
449    /// Initialize an empty builder calling `fun`, rooted unless it is a symbol.
450    ///
451    /// `fun` may be a freshly allocated, unprotected closure. The temporary
452    /// stack root keeps it alive while the pool allocates its backing VECSXP.
453    #[inline]
454    unsafe fn from_callable(fun: SEXP) -> Self {
455        unsafe {
456            if !needs_root(fun) {
457                return RCall {
458                    fun,
459                    args: Vec::new(),
460                    roots: ProtectPool::new(4),
461                };
462            }
463            let fun_guard = OwnedProtect::new(fun);
464            let mut roots = ProtectPool::new(4);
465            roots.insert(fun_guard.get());
466            RCall {
467                fun,
468                args: Vec::new(),
469                roots,
470            }
471        }
472    }
473
474    /// Start building a call to a named R function.
475    ///
476    /// The function is looked up via `Rf_install`, which returns an interned symbol.
477    ///
478    /// # Safety
479    ///
480    /// Must be called from the R main thread.
481    ///
482    /// # Panics
483    ///
484    /// Panics if `fun_name` contains a null byte.
485    #[inline]
486    pub unsafe fn new(fun_name: &str) -> Self {
487        let c_name = CString::new(fun_name).expect("function name must not contain null bytes");
488        unsafe { Self::from_callable(Rf_install(c_name.as_ptr())) }
489    }
490
491    /// Start building a call to a function given as a C string literal.
492    ///
493    /// More efficient than [`new`](Self::new) when a `&CStr` is available.
494    ///
495    /// # Safety
496    ///
497    /// Must be called from the R main thread.
498    #[inline]
499    pub unsafe fn from_cstr(fun_name: &CStr) -> Self {
500        unsafe { Self::from_callable(Rf_install(fun_name.as_ptr())) }
501    }
502
503    /// Start building a call with a function SEXP (closure, builtin, etc.).
504    ///
505    /// # Safety
506    ///
507    /// `fun` must be a valid SEXP representing a callable R object.
508    #[inline]
509    pub unsafe fn from_sexp(fun: SEXP) -> Self {
510        unsafe { Self::from_callable(fun) }
511    }
512
513    /// Root an argument for the builder's lifetime, unless it is a symbol.
514    ///
515    /// `value` may be fresh: the pool keeps it reachable while it grows.
516    #[inline]
517    fn root(&mut self, value: SEXP) {
518        if needs_root(value) {
519            self.roots.root(value);
520        }
521    }
522
523    /// Add a positional argument.
524    #[inline]
525    pub fn arg(mut self, value: SEXP) -> Self {
526        self.root(value);
527        self.args.push((None, value));
528        self
529    }
530
531    /// Add a named argument.
532    ///
533    /// # Panics
534    ///
535    /// Panics if `name` contains a null byte.
536    #[inline]
537    pub fn named_arg(mut self, name: &str, value: SEXP) -> Self {
538        let c_name = CString::new(name).expect("argument name must not contain null bytes");
539        self.root(value);
540        self.args.push((Some(c_name), value));
541        self
542    }
543
544    /// Convert a Rust value and add it as a positional argument.
545    ///
546    /// The converted value is rooted before anything else allocates, so a
547    /// value made only for this call needs no root of its own. Write
548    /// `RCall::new("qt").arg_value(p).arg_value(df)`, not
549    /// `p.into_sexp()` and `df.into_sexp()` first: the second conversion, and
550    /// [`new`](Self::new), allocate while the first value is not rooted.
551    ///
552    /// # Panics
553    ///
554    /// Panics if the conversion fails, as [`IntoR::into_sexp`] does.
555    #[inline]
556    pub fn arg_value(self, value: impl IntoR) -> Self {
557        self.arg(value.into_sexp())
558    }
559
560    /// Convert a Rust value and add it as a named argument. See
561    /// [`arg_value`](Self::arg_value).
562    ///
563    /// # Panics
564    ///
565    /// Panics if `name` contains a null byte, or if the conversion fails.
566    #[inline]
567    pub fn named_value(self, name: &str, value: impl IntoR) -> Self {
568        self.named_arg(name, value.into_sexp())
569    }
570
571    /// Add R's `...` at this position: the dots of the frame the call is
572    /// evaluated in, each passed on as the promise R matched, unevaluated.
573    ///
574    /// Only [`LazyDots::forward`](crate::LazyDots::forward) /
575    /// [`try_forward`](crate::LazyDots::try_forward) evaluate a call in a
576    /// frame that has dots (the generated wrapper's), and they add `...` last
577    /// themselves, so this is for putting it elsewhere:
578    /// `RCall::new("f").dots().named_value("na.rm", true)` is
579    /// `f(..., na.rm = TRUE)`. Evaluated in an environment without dots, R
580    /// raises "'...' used in an incorrect context".
581    #[inline]
582    pub fn dots(self) -> Self {
583        // SAFETY: symbols are interned for the session and need no root.
584        self.arg(unsafe { dots_symbol() })
585    }
586
587    /// Whether the call passes `...` ([`dots`](Self::dots)).
588    pub(crate) fn has_dots(&self) -> bool {
589        // SAFETY: as in `dots`.
590        let dots = unsafe { dots_symbol() };
591        self.args
592            .iter()
593            .any(|(name, value)| name.is_none() && *value == dots)
594    }
595
596    /// Add a positional argument passed as is: `quote(<value>)`.
597    ///
598    /// The call evaluates its arguments, so a symbol, a call or a quosure
599    /// added with [`arg`](Self::arg) is evaluated before the function sees
600    /// it. Use this for a language object the function must receive as a
601    /// value: a call for an `error_call` / `call` argument, an expression
602    /// for `eval()`. Any other value passes through `quote()` unchanged.
603    #[inline]
604    pub fn quoted_arg(self, value: SEXP) -> Self {
605        let quoted = unsafe { quote(value) };
606        self.arg(quoted)
607    }
608
609    /// Add a named argument passed as is: `name = quote(<value>)`. See
610    /// [`quoted_arg`](Self::quoted_arg).
611    ///
612    /// # Panics
613    ///
614    /// Panics if `name` contains a null byte.
615    #[inline]
616    pub fn named_quoted_arg(self, name: &str, value: SEXP) -> Self {
617        let quoted = unsafe { quote(value) };
618        self.named_arg(name, quoted)
619    }
620
621    /// Build the LANGSXP without evaluating it.
622    ///
623    /// The returned SEXP is **unprotected**. The caller must protect it if
624    /// further allocations will occur before use.
625    ///
626    /// # Safety
627    ///
628    /// Must be called from the R main thread. The builder keeps the callable
629    /// and every argument rooted until it is dropped.
630    pub unsafe fn build(&self) -> SEXP {
631        unsafe {
632            // Build the argument pairlist from back to front using Rf_cons.
633            // ProtectScope tracks all intermediate cons cells and the final
634            // LANGSXP head, then unprotects them all on drop. The returned
635            // call is unprotected — caller protects it if needed.
636            let scope = ProtectScope::new();
637
638            let mut tail = SEXP::nil();
639            for (name, value) in self.args.iter().rev() {
640                tail = scope.protect_raw(value.cons(tail));
641                if let Some(c_name) = name {
642                    tail.set_tag(Rf_install(c_name.as_ptr()));
643                }
644            }
645
646            // Prepend the function as LANGSXP head.
647            // ProtectScope drops here; the call is unprotected on return
648            // (callers re-protect via OwnedProtect before invoking eval).
649            scope.protect_raw(self.fun.lcons(tail))
650        }
651    }
652
653    /// Evaluate the call in the given environment, catching an R error.
654    ///
655    /// The call runs in a new top-level context (`R_ToplevelExec`) under an
656    /// exiting handler for `error` conditions (`R_tryCatchError`). So the
657    /// caller's condition handlers and restarts don't see it, and an R error
658    /// comes back as `Err` holding R's condition (its message, call, classes
659    /// and the object itself) instead of jumping through the Rust frames.
660    /// Use [`eval_with_handlers`](Self::eval_with_handlers) for a call whose
661    /// conditions the user should see as raised, and
662    /// [`try_eval_with_handlers`](Self::try_eval_with_handlers) for one whose
663    /// warnings and messages the user should see but whose error the Rust
664    /// code handles.
665    ///
666    /// ```ignore
667    /// match RCall::new("print").arg(df).named_arg("na.print", one).eval_base() {
668    ///     Ok(_) => {}
669    ///     // `e` is R's condition: "invalid 'na.print' specification",
670    ///     // without the "Error in print.default(...) :" prefix.
671    ///     Err(e) => rust_error!(class = e.reraise_class("pkg_print_error"), "{e}"),
672    /// }
673    /// ```
674    ///
675    /// # Safety
676    ///
677    /// - Must be called from the R main thread.
678    /// - `env` must be a valid ENVSXP.
679    /// - The builder must not outlive the active R session.
680    ///
681    /// # Returns
682    ///
683    /// - `Ok(SEXP)` with the result (unprotected — caller should protect if needed)
684    /// - `Err(REvalError)` with R's error condition on failure
685    pub unsafe fn eval(&self, env: SEXP) -> Result<SEXP, REvalError> {
686        unsafe {
687            let call = OwnedProtect::new(self.build());
688            try_eval(call.get(), env)
689        }
690    }
691
692    /// Evaluate in `R_BaseEnv`.
693    ///
694    /// # Safety
695    ///
696    /// Same as [`eval`](Self::eval).
697    #[inline]
698    pub unsafe fn eval_base(&self) -> Result<SEXP, REvalError> {
699        unsafe { self.eval(R_BaseEnv) }
700    }
701
702    /// Evaluate the call in `env`, as [`eval`](Self::eval) does, and read the
703    /// result as `T`.
704    ///
705    /// The result stays rooted while it is converted, so the caller never
706    /// holds it unrooted:
707    ///
708    /// ```ignore
709    /// let q: Vec<Option<f64>> = RCall::new("qt")
710    ///     .arg_value(p)
711    ///     .arg_value(df)
712    ///     .eval_as(stats.as_sexp())?;
713    /// ```
714    ///
715    /// A result that `T` can't read is an `Err` too: a `simpleError`
716    /// whose message is the conversion's, with no call. A `T` that only
717    /// views the value (`SEXP`, `List`, `DataFrame`) comes back unrooted, as
718    /// from [`eval`](Self::eval); read into an owned type (`Vec<T>`,
719    /// `String`, a struct) or protect it.
720    ///
721    /// # Safety
722    ///
723    /// As [`eval`](Self::eval).
724    pub unsafe fn eval_as<T>(&self, env: SEXP) -> Result<T, REvalError>
725    where
726        T: TryFromSexp,
727        T::Error: std::fmt::Display,
728    {
729        unsafe {
730            let value = OwnedProtect::new(self.eval(env)?);
731            T::try_from_sexp(value.get()).map_err(|e| REvalError::simple_error(&e.to_string()))
732        }
733    }
734
735    /// [`eval_as`](Self::eval_as) in `R_BaseEnv`.
736    ///
737    /// # Safety
738    ///
739    /// As [`eval`](Self::eval).
740    #[inline]
741    pub unsafe fn eval_base_as<T>(&self) -> Result<T, REvalError>
742    where
743        T: TryFromSexp,
744        T::Error: std::fmt::Display,
745    {
746        unsafe { self.eval_as(R_BaseEnv) }
747    }
748
749    /// Evaluate the call in `env` in the caller's R context, keeping R's
750    /// condition handlers: see [`eval_with_handlers`].
751    ///
752    /// Use it to call R code that signals conditions the user should see, with
753    /// their classes, through their own `withCallingHandlers()` /
754    /// `tryCatch()`. A tidyselect selection, say:
755    ///
756    /// ```ignore
757    /// // `cols` is a `Quosure` parameter, `data` a data frame.
758    /// let selected = unsafe {
759    ///     RCall::namespaced("tidyselect", "eval_select")?
760    ///         .arg(cols.sexp())
761    ///         .arg(data)
762    ///         .eval_with_handlers(R_BaseEnv)
763    /// };
764    /// ```
765    ///
766    /// A column that does not exist raises tidyselect's own error (class
767    /// `vctrs_error_subscript_oob`, with its call), which unwinds through the
768    /// Rust frames and reaches the user's `tryCatch()` as it was raised. To
769    /// get the error back as `Err(REvalError)` instead, while the user's
770    /// handlers still see warnings and messages, use
771    /// [`try_eval_with_handlers`](Self::try_eval_with_handlers).
772    ///
773    /// # Safety
774    ///
775    /// As [`eval_with_handlers`]: R's main thread, inside a miniextendr
776    /// boundary, `env` an environment. The result is unprotected.
777    pub unsafe fn eval_with_handlers(&self, env: SEXP) -> SEXP {
778        unsafe {
779            let call = OwnedProtect::new(self.build());
780            eval_with_handlers(call.get(), env)
781        }
782    }
783
784    /// Evaluate the call in `env` in the caller's R context, keeping R's
785    /// condition handlers, with an R error returned as `Err`: see
786    /// [`try_eval_with_handlers`].
787    ///
788    /// Use it for a call that forwards the user's arguments to an R function
789    /// and raises the function's errors as the package's own: the user's
790    /// `withCallingHandlers()`, `suppressWarnings()` and
791    /// `tryCatch(warning = )` see the function's warnings and messages, and
792    /// an R error comes back as [`REvalError`]. A `[` method of a data-frame
793    /// subclass, say:
794    ///
795    /// ```ignore
796    /// let out = unsafe {
797    ///     RCall::new("[")
798    ///         .arg(frame)
799    ///         .arg(i)
800    ///         .named_arg("drop", drop)
801    ///         .try_eval_with_handlers(R_BaseEnv)
802    /// }
803    /// .unwrap_or_else(|e| rust_error!(class = e.reraise_class("pkg_subset_error"), "{e}"));
804    /// ```
805    ///
806    /// `x[1, drop = FALSE]` warns that `drop` is ignored; the user's
807    /// `suppressWarnings()` silences it. A column that does not exist
808    /// ("undefined columns selected") comes back as `Err` and is raised again
809    /// under `pkg_subset_error`.
810    ///
811    /// # Safety
812    ///
813    /// As [`try_eval_with_handlers`]: R's main thread, inside a miniextendr
814    /// boundary, `env` an environment. The value is unprotected.
815    pub unsafe fn try_eval_with_handlers(&self, env: SEXP) -> Result<SEXP, REvalError> {
816        unsafe {
817            let call = OwnedProtect::new(self.build());
818            try_eval_with_handlers(call.get(), env)
819        }
820    }
821
822    /// Start building a namespaced call: `pkg::fun(args…)`.
823    ///
824    /// Looks up `pkg::fun_name` in the base environment and uses the resolved
825    /// function closure as the call target. This respects R's namespace lookup
826    /// rules (exported + non-exported via `::` / `:::`).
827    ///
828    /// This is the runtime counterpart of the lowered `pkg::fn(args…)` form
829    /// in `r!(pkg::fn(args…))`.
830    ///
831    /// # Safety
832    ///
833    /// Must be called from the R main thread.
834    ///
835    /// # Panics
836    ///
837    /// Panics if `pkg` or `fun_name` contain a null byte.
838    ///
839    /// # Errors
840    ///
841    /// R's error if the namespace lookup fails (package not installed, or no
842    /// such function).
843    pub unsafe fn namespaced(pkg: &str, fun_name: &str) -> Result<Self, REvalError> {
844        unsafe {
845            // Build (:: pkg fun_name) as a LANGSXP and evaluate it to get the closure.
846            let ns_op = Rf_install(c"::".as_ptr());
847            let pkg_sym = {
848                let c = CString::new(pkg).expect("pkg name must not contain null bytes");
849                Rf_install(c.as_ptr())
850            };
851            let fun_sym = {
852                let c = CString::new(fun_name).expect("fun name must not contain null bytes");
853                Rf_install(c.as_ptr())
854            };
855
856            // Build `(:: pkg fun_name)` pairlist from back to front.
857            let scope = crate::gc_protect::ProtectScope::new();
858            let nil = crate::sys::R_NilValue;
859            let fun_cons = scope.protect_raw(fun_sym.cons(nil));
860            let pkg_cons = scope.protect_raw(pkg_sym.cons(fun_cons));
861            let ns_call = scope.protect_raw(ns_op.lcons(pkg_cons));
862
863            // Evaluate to resolve the function closure.
864            let fun_sexp = try_eval(ns_call, R_BaseEnv)?;
865
866            // Root the resolved closure for the builder lifetime. This is
867            // load-bearing even for closures that are not namespace bindings
868            // (the same constructor backs `RCall::from_sexp`).
869            Ok(RCall::from_sexp(fun_sexp))
870        }
871    }
872}
873
874/// The symbol `...`.
875///
876/// # Safety
877///
878/// R's main thread. Symbols are never collected.
879unsafe fn dots_symbol() -> SEXP {
880    unsafe { Rf_install(c"...".as_ptr()) }
881}
882
883/// Build and evaluate `target$name` — the R `$` extraction operator.
884///
885/// This is a convenience wrapper that avoids hand-rolling
886/// `Rf_install("$") + Rf_lang3(...) + R_tryEval(...)` ladders.
887/// Equivalent to:
888///
889/// ```ignore
890/// RCall::new("$")
891///     .arg(target)
892///     .arg(SEXP::scalar_string_from_str(name))
893///     .eval_base()
894/// ```
895///
896/// with the name argument protected while the call is built.
897///
898/// # Safety
899///
900/// - Must be called from the R main thread.
901/// - `target` must be a valid SEXP (typically a list, environment, or S4
902///   object that supports `$` extraction).
903///
904/// # Returns
905///
906/// - `Ok(SEXP)` with the extracted value (unprotected — caller should protect if needed).
907/// - `Err(REvalError)` with R's error if `$` extraction fails.
908pub unsafe fn dollar_extract(target: SEXP, name: &str) -> Result<SEXP, REvalError> {
909    unsafe {
910        let name_sexp = OwnedProtect::new(SEXP::scalar_string_from_str(name));
911        RCall::new("$").arg(target).arg(name_sexp.get()).eval_base()
912    }
913}
914// endregion
915
916/// Whether `value` needs a GC root while Rust holds it across an allocation.
917///
918/// A symbol never does: R never frees one. `install()` links every new symbol
919/// into `R_SymbolTable` (`src/main/names.c`, `install`, and `installNoTrChar`
920/// likewise), nothing ever unlinks it, and the collector marks every bucket of
921/// the table on every collection (`src/main/memory.c`, `RunGenCollect`: the
922/// `R_SymbolTable` loop). The marker symbols outside the table
923/// (`R_MissingArg`, `R_UnboundValue`, `R_RestartToken` and the rest, made by
924/// `mkSymMarker` in `InitNames`) are forwarded as roots of their own in the
925/// same function.
926#[inline]
927fn needs_root(value: SEXP) -> bool {
928    value.type_of() != crate::SEXPTYPE::SYMSXP
929}
930
931/// `quote(<value>)`, unprotected. `value` is rooted across the allocation
932/// unless it is a symbol ([`needs_root`]).
933///
934/// # Safety
935///
936/// R's main thread.
937unsafe fn quote(value: SEXP) -> SEXP {
938    unsafe {
939        let quote = Rf_install(c"quote".as_ptr());
940        if !needs_root(value) {
941            return crate::sys::Rf_lang2(quote, value);
942        }
943        let value = OwnedProtect::new(value);
944        crate::sys::Rf_lang2(quote, value.get())
945    }
946}
947
948// region: eval_with_handlers, try_eval_with_handlers (evaluation in the caller's R context)
949
950/// Evaluate `expr` in `env` in the caller's R context, the way R code calling
951/// `eval(expr, env)` does.
952///
953/// | | [`RCall::eval`], [`r_eval_str`] | `eval_with_handlers` | [`try_eval_with_handlers`] |
954/// |---|---|---|---|
955/// | R entry point | `R_tryCatchError` inside `R_ToplevelExec` | `Rf_eval` in its own `R_UnwindProtect` | `R_tryCatchError` in its own `R_UnwindProtect` |
956/// | caller's `withCallingHandlers()`, `suppressWarnings()` | not seen (`R_ToplevelExec` empties the handler and restart stacks) | see every warning, message and condition | see every warning, message and condition except an error |
957/// | R error | `Err(`[`REvalError`]`)`: R's condition, with its message (no `Error in` prefix, no `Calls:` line), call and classes; the Rust code decides what to raise | unwinds as the R condition, class and call kept | `Err(`[`REvalError`]`)`, as from [`RCall::eval`] |
958/// | caller's `tryCatch(warning = )`, restarts, interrupts | not seen (an interrupt ends the evaluation with an `Err`) | exit through the Rust frames | exit through the Rust frames |
959///
960/// An R error, or any other exit from the evaluation (an exiting handler of
961/// the caller's `tryCatch()`, `invokeRestart()`, an interrupt), stops at this
962/// function's own `R_UnwindProtect` frame, so it never jumps over a Rust
963/// frame. It then leaves as a Rust unwind: the frames between here and the
964/// `#[miniextendr]` boundary drop their values (a guard's `Drop` runs), and
965/// the boundary hands the exit back to R, which carries on to the handler or
966/// restart it was going to, with the original condition object. A warning or
967/// message the caller muffles returns here and the evaluation continues.
968///
969/// So this is the evaluator for code a user wrote: an argument passed
970/// unevaluated ([`crate::Quoted`], [`crate::Quosure`]), a callback, a call into
971/// a package whose conditions are part of its interface. Keep [`RCall::eval`]
972/// for internal calls whose failure the Rust code handles itself, for
973/// instance by raising its own condition with the caught message
974/// ([`REvalError::reraise_class`]). [`try_eval_with_handlers`] combines the
975/// two: the caller's handlers see warnings and messages, and an R error comes
976/// back as `Err` for the Rust code to raise.
977///
978/// The unwind must reach a miniextendr boundary. Don't catch it with
979/// `std::panic::catch_unwind` in between, or the R exit is abandoned (R
980/// carries on as after `R_tryEval`, without running the caller's handler);
981/// resume a caught payload with `std::panic::resume_unwind` on the same
982/// thread before the call returns. An exit resumed in a later call has lost
983/// its target: the boundary raises an error instead. Don't call this from a
984/// `Drop` implementation: an exit there would start an unwind during an
985/// unwind, which aborts.
986///
987/// # Safety
988///
989/// - R's main thread, inside a miniextendr boundary: a `#[miniextendr]`
990///   function body, a [`with_r_unwind_protect`](crate::unwind_protect::with_r_unwind_protect)
991///   closure, an ALTREP callback with the `r_unwind` or `rust_unwind` guard
992///   (not `unsafe`, which catches nothing), a connection callback, a
993///   [`with_r_thread`](crate::worker::with_r_thread) closure.
994/// - `expr` and `env` stay rooted for the call; `env` is an environment.
995///
996/// # Returns
997///
998/// The value, **unprotected**: protect it before the next allocation.
999pub unsafe fn eval_with_handlers(expr: SEXP, env: SEXP) -> SEXP {
1000    unsafe { crate::unwind_protect::eval_unwinding(expr, env) }
1001}
1002
1003/// Evaluate `expr` in `env` in the caller's R context, as
1004/// [`eval_with_handlers`] does, but return an R error as `Err` instead of
1005/// letting it unwind.
1006///
1007/// The caller's `withCallingHandlers()`, `suppressWarnings()` and
1008/// `suppressMessages()` see the evaluation's warnings, messages and other
1009/// conditions; one they muffle returns here and the evaluation goes on. A
1010/// condition of class `error` is caught at the evaluation, by an exiting
1011/// handler (`R_tryCatchError`), and comes back as `Err(`[`REvalError`]`)`
1012/// built as [`RCall::eval`] builds it: R's condition, with its message (no
1013/// `Error in` prefix, no `Calls:` line), call and classes. The Rust code
1014/// decides what to raise ([`REvalError::reraise_class`]).
1015///
1016/// Any other exit (an exiting handler of the caller's `tryCatch()`, such as
1017/// `tryCatch(warning = )`, `invokeRestart()`, an interrupt) leaves as under
1018/// [`eval_with_handlers`]: it stops at this function's own `R_UnwindProtect`
1019/// frame and goes on as a Rust unwind, the frames up to the `#[miniextendr]`
1020/// boundary drop their values, and the boundary hands the exit back to R.
1021///
1022/// The error handler is the innermost one, so a handler of the caller's for
1023/// errors (`withCallingHandlers(error = )`, `tryCatch(error = )`) does not see
1024/// an error the evaluation raises. An error raised by a handler of the
1025/// caller's, as in `withCallingHandlers(warning = function(w) stop("no"))`,
1026/// is not the evaluation's: R runs that handler outside the evaluation's
1027/// handlers, so the error unwinds to the caller as raised.
1028///
1029/// The evaluation runs inside R's `tryCatch()`, as under [`RCall::eval`]: an
1030/// error raised at the top of `expr` (`stop()` called directly, not from a
1031/// function) has no [`call`](REvalError::call), and code at the top of `expr`
1032/// that inspects the call stack (`sys.call()`) sees the `tryCatch()` frames.
1033///
1034/// The rules of [`eval_with_handlers`] for the unwind hold here too: don't
1035/// catch it with `std::panic::catch_unwind` on its way to the boundary, and
1036/// don't call this from a `Drop` implementation.
1037///
1038/// # Safety
1039///
1040/// As [`eval_with_handlers`]:
1041///
1042/// - R's main thread, inside a miniextendr boundary: a `#[miniextendr]`
1043///   function body, a [`with_r_unwind_protect`](crate::unwind_protect::with_r_unwind_protect)
1044///   closure, an ALTREP callback with the `r_unwind` or `rust_unwind` guard
1045///   (not `unsafe`, which catches nothing), a connection callback, a
1046///   [`with_r_thread`](crate::worker::with_r_thread) closure.
1047/// - `expr` and `env` stay rooted for the call; `env` is an environment.
1048///
1049/// # Returns
1050///
1051/// - `Ok(SEXP)` with the value, **unprotected**: protect it before the next
1052///   allocation.
1053/// - `Err(REvalError)` with R's error condition.
1054pub unsafe fn try_eval_with_handlers(expr: SEXP, env: SEXP) -> Result<SEXP, REvalError> {
1055    unsafe { into_result(try_eval_unwinding(expr, env)) }
1056}
1057
1058// endregion
1059
1060// region: r_eval_str (runtime string parse + eval)
1061
1062/// Parse a string of R source and evaluate it in `env`.
1063///
1064/// This is the runtime workhorse behind the [`r_str!`](crate::r_str) and
1065/// [`r!`](crate::r) macros. It performs the full
1066/// `R_ParseVector` → check status → `Rf_eval` ladder with correct GC
1067/// protection on every intermediate SEXP, so callers never have to hand-roll
1068/// `OwnedProtect` around the parse tree.
1069///
1070/// Only the **last** top-level expression's value is returned (matching R's
1071/// `eval(parse(text = ...))` semantics): each parsed expression is evaluated in
1072/// order so that side effects (assignments, `library()`, …) take effect, and
1073/// the value of the final one is returned. An empty / whitespace-only string
1074/// yields `R_NilValue`.
1075///
1076/// # Safety
1077///
1078/// - Must be called from (or routed to) the R main thread. The parse and eval
1079///   FFI calls go through the checked `#[r_ffi_checked]` variants, which
1080///   serialize onto the R thread via `with_r_thread`, so calling from a
1081///   worker thread is sound — but the returned SEXP must not outlive the R
1082///   session.
1083/// - `env` must be a valid ENVSXP.
1084///
1085/// # Returns
1086///
1087/// - `Ok(SEXP)` with the value of the last expression (**unprotected** — the
1088///   caller should protect it if further allocations will occur before use).
1089/// - `Err(REvalError)` if evaluation raises an R error: R's condition, caught
1090///   as in [`RCall::eval`], so it never longjmps through Rust frames. A parse
1091///   failure (syntax error / incomplete input) has no R condition; it comes
1092///   back as a `simpleError` whose message names the failure and the source.
1093///
1094/// # Example
1095///
1096/// ```ignore
1097/// use miniextendr_api::expression::r_eval_str;
1098/// use miniextendr_api::sys::R_GlobalEnv;
1099///
1100/// unsafe {
1101///     let three = r_eval_str("1L + 2L", R_GlobalEnv)?;
1102///     // three is an INTSXP holding 3
1103/// }
1104/// ```
1105pub unsafe fn r_eval_str(code: &str, env: SEXP) -> Result<SEXP, REvalError> {
1106    unsafe {
1107        // 1. Wrap the source in a length-1 STRSXP. scalar_string_from_str
1108        //    allocates a CHARSXP + STRSXP; protect it across the parse, which
1109        //    allocates again.
1110        let code_sexp = OwnedProtect::new(SEXP::scalar_string_from_str(code));
1111
1112        // 2. Parse. R_ParseVector returns an EXPRSXP (a vector of expressions).
1113        //    Protect it across the subsequent VECTOR_ELT / Rf_eval allocations.
1114        let mut status = ParseStatus::PARSE_NULL;
1115        let parsed = R_ParseVector(code_sexp.get(), -1, &mut status, sys::R_NilValue);
1116
1117        let parse_failure = match status {
1118            ParseStatus::PARSE_OK => None,
1119            ParseStatus::PARSE_INCOMPLETE => Some(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("incomplete R expression (unbalanced delimiter?): {0}",
                code))
    })format!(
1120                "incomplete R expression (unbalanced delimiter?): {code}"
1121            )),
1122            ParseStatus::PARSE_ERROR => Some(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("R syntax error while parsing: {0}",
                code))
    })format!("R syntax error while parsing: {code}")),
1123            ParseStatus::PARSE_EOF => {
1124                Some(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("unexpected end of input while parsing: {0}",
                code))
    })format!("unexpected end of input while parsing: {code}"))
1125            }
1126            ParseStatus::PARSE_NULL => {
1127                Some(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("R_ParseVector returned PARSE_NULL for: {0}",
                code))
    })format!("R_ParseVector returned PARSE_NULL for: {code}"))
1128            }
1129        };
1130        if let Some(message) = parse_failure {
1131            return Err(REvalError::simple_error(&message));
1132        }
1133
1134        let parsed = OwnedProtect::new(parsed);
1135
1136        // 3. Evaluate each parsed expression in order; return the value of the
1137        //    last one. An empty EXPRSXP (blank source) yields R_NilValue.
1138        let n = parsed.get().xlength();
1139        let mut result = sys::R_NilValue;
1140        for i in 0..n {
1141            // VECTOR_ELT borrows from `parsed` (still protected). The element
1142            // is part of the protected EXPRSXP, so it stays reachable.
1143            let expr = parsed.get().vector_elt(i);
1144            result = try_eval(expr, env)?;
1145        }
1146
1147        Ok(result)
1148    }
1149}
1150
1151/// Parse and evaluate a string of R source in `R_GlobalEnv`.
1152///
1153/// Convenience wrapper over [`r_eval_str`] for the common case. See that
1154/// function for safety and return semantics.
1155///
1156/// # Safety
1157///
1158/// Same as [`r_eval_str`].
1159#[inline]
1160pub unsafe fn r_eval_str_global(code: &str) -> Result<SEXP, REvalError> {
1161    unsafe { r_eval_str(code, R_GlobalEnv) }
1162}
1163// endregion
1164
1165// region: REvalError (an R error caught by RCall::eval / r_eval_str)
1166
1167/// The trailing classes [`REvalError::specific_classes`] leaves out: R's base
1168/// error layers, and the `rust_error` layer a miniextendr function adds.
1169/// `error!` and a `Result` return add them again.
1170const BASE_ERROR_CLASSES: [&str; 4] = ["rust_error", "simpleError", "error", "condition"];
1171
1172/// The message of the `simpleError` that stands for an evaluation that jumped
1173/// to the top level without an error condition.
1174const EXIT_MESSAGE: &str =
1175    "R evaluation was interrupted (it jumped to the top level without an error condition)";
1176
1177/// An R error caught by [`RCall::eval`] / [`RCall::eval_base`], [`r_eval_str`],
1178/// the helpers built on them ([`REnv::package_namespace`],
1179/// [`RCall::namespaced`], [`dollar_extract`]), and [`try_eval_with_handlers`] /
1180/// [`RCall::try_eval_with_handlers`].
1181///
1182/// It holds R's condition object, rooted while this value lives, and what R
1183/// reports about it:
1184///
1185/// - [`message`](Self::message): `conditionMessage(cond)`, the text R prints
1186///   after `Error in <call> :`, without that prefix and without the `Calls:`
1187///   traceback line of a non-interactive session;
1188/// - [`call`](Self::call): `conditionCall(cond)`, `None` when it is `NULL`;
1189/// - [`classes`](Self::classes): the class vector, most specific first;
1190/// - [`condition`](Self::condition): the condition object itself.
1191///
1192/// `Display` writes the message alone. Every value holds a condition: a parse
1193/// failure in [`r_eval_str`] and an evaluation under [`RCall::eval`] that
1194/// jumps to the top level without an error (an interrupt) come back as a
1195/// `simpleError` built for them, with no call.
1196///
1197/// # Raising your own condition with it
1198///
1199/// The condition macros take the message through `Display`, and
1200/// [`reraise_class`](Self::reraise_class) puts the package's own classes
1201/// ahead of the caught ones:
1202///
1203/// ```ignore
1204/// use miniextendr_api::rust_error;
1205///
1206/// match RCall::new("print").arg(x).named_arg("na.print", na).eval_base() {
1207///     Ok(_) => {}
1208///     // The package's own class with R's message:
1209///     //   class = "pkg_print_error"
1210///     // or with the caught classes kept after it, so a handler for those
1211///     // still matches:
1212///     Err(e) => rust_error!(class = e.reraise_class("pkg_print_error"), "{e}"),
1213/// }
1214/// ```
1215///
1216/// [`reraise`](Self::reraise) raises the caught condition itself instead, as
1217/// R raised it.
1218///
1219/// A `#[miniextendr]` function returning `Result<T, REvalError>` raises the
1220/// caught message with the caught [specific classes](Self::specific_classes)
1221/// (`REvalError` implements [`RConditionData`](crate::condition::RConditionData)).
1222/// For a `Result<T, condition::RError>` return, `RError::from(e)` takes the
1223/// message (it is a `std::error::Error`) and `.class(...)` adds the classes.
1224///
1225/// # Threads
1226///
1227/// `!Send + !Sync`: dropping the value releases its root on R's precious list,
1228/// which is R main-thread state, and [`condition`](Self::condition) /
1229/// [`call`](Self::call) hand out SEXPs. To carry the failure to another thread
1230/// or into a `Send` error type (`anyhow::Error`, `Box<dyn Error + Send>`),
1231/// take [`message`](Self::message) or convert into
1232/// [`condition::RError`](crate::condition::RError).
1233pub struct REvalError {
1234    /// `conditionMessage(cond)`.
1235    message: String,
1236    /// `class(cond)`.
1237    classes: Vec<String>,
1238    /// `list(cond, call)`, on R's precious list while this value lives, where
1239    /// `call` is `conditionCall(cond)` or `NULL` (see [`REvalError::call`]).
1240    /// Its handle makes the value `!Send + !Sync`, see the type docs.
1241    roots: crate::Preserved,
1242}
1243
1244impl REvalError {
1245    /// The condition's message (`conditionMessage(cond)`), without R's
1246    /// `Error in <call> :` prefix and `Calls:` line.
1247    #[inline]
1248    pub fn message(&self) -> &str {
1249        &self.message
1250    }
1251
1252    /// The condition's call (`conditionCall(cond)`), `None` when it is `NULL`.
1253    ///
1254    /// Also `None` for an error raised at the top of the evaluated expression
1255    /// (`stop()` called directly rather than from a function): R gives such
1256    /// an error the call of the `tryCatch()` frame the evaluation runs in,
1257    /// `doTryCatch(return(expr), name, parentenv, handler)`, which is no call
1258    /// of the evaluated code. The [condition object](Self::condition) keeps
1259    /// what R recorded.
1260    ///
1261    /// The SEXP is rooted while this value lives; protect it to keep it longer.
1262    #[inline]
1263    pub fn call(&self) -> Option<SEXP> {
1264        let call = self.roots.vector_elt(1);
1265        (!call.is_nil()).then_some(call)
1266    }
1267
1268    /// The condition's class vector, most specific first: for `stop("boom")`,
1269    /// `["simpleError", "error", "condition"]`.
1270    #[inline]
1271    pub fn classes(&self) -> &[String] {
1272        &self.classes
1273    }
1274
1275    /// Whether the condition's class vector contains `class`.
1276    #[inline]
1277    pub fn inherits(&self, class: &str) -> bool {
1278        self.classes.iter().any(|c| c == class)
1279    }
1280
1281    /// The classes ahead of the base layers: the class vector without its
1282    /// trailing `rust_error`, `simpleError`, `error` and `condition`, which the
1283    /// condition macros add again. Empty for `stop("boom")`; `["my_class"]`
1284    /// for `stop(errorCondition("boom", class = "my_class"))` or for a
1285    /// miniextendr function's `error!(class = "my_class", "boom")`.
1286    pub fn specific_classes(&self) -> &[String] {
1287        let end = self
1288            .classes
1289            .iter()
1290            .rposition(|c| !BASE_ERROR_CLASSES.contains(&c.as_str()))
1291            .map_or(0, |last| last + 1);
1292        &self.classes[..end]
1293    }
1294
1295    /// The class vector for raising this error again as the package's own
1296    /// condition: `own` first (one class or several, most specific first),
1297    /// then the caught [specific classes](Self::specific_classes) not already
1298    /// in `own`.
1299    ///
1300    /// ```ignore
1301    /// // stop(errorCondition("boom", class = "vctrs_error")) caught as `e`:
1302    /// rust_error!(class = e.reraise_class(["pkg_print_error", "pkg_error"]), "{e}");
1303    /// // class: pkg_print_error, pkg_error, vctrs_error, rust_error,
1304    /// //        simpleError, error, condition
1305    /// ```
1306    pub fn reraise_class(&self, own: impl ConditionClass) -> Vec<String> {
1307        let mut classes = own.into_condition_class();
1308        for class in self.specific_classes() {
1309            if !classes.contains(class) {
1310                classes.push(class.clone());
1311            }
1312        }
1313        classes
1314    }
1315
1316    /// The condition object.
1317    ///
1318    /// The SEXP is rooted while this value lives; protect it to keep it longer.
1319    #[inline]
1320    pub fn condition(&self) -> SEXP {
1321        self.roots.vector_elt(0)
1322    }
1323
1324    /// Raise the caught condition again, as R raised it: its classes, message
1325    /// and fields are unchanged, and its call is [`call`](Self::call). So an
1326    /// error raised at the top of the evaluated expression reports no call,
1327    /// not R's `doTryCatch(...)` frame.
1328    ///
1329    /// Use it for the errors a body doesn't explain itself, next to a
1330    /// package's own condition for the ones it does
1331    /// ([`Quoted::try_eval_in`](crate::Quoted::try_eval_in) has an example).
1332    /// Unlike `rust_error!(class = e.reraise_class(..), "{e}")`, the condition
1333    /// gets no `rust_error` class and keeps R's call, not the wrapper's.
1334    ///
1335    /// The condition is signalled by base `stop(cond)`, evaluated as
1336    /// [`eval_with_handlers`] evaluates: the caller's handlers see it, and the
1337    /// exit stops at that evaluation's `R_UnwindProtect` frame and leaves as a
1338    /// Rust unwind. The frames up to the `#[miniextendr]` boundary drop their
1339    /// values, and the boundary hands the exit back to R, which goes on to the
1340    /// caller's handler or to the top level. The same rules hold as for
1341    /// [`eval_with_handlers`]: don't catch the unwind with
1342    /// `std::panic::catch_unwind`, and don't call this from a `Drop`
1343    /// implementation.
1344    ///
1345    /// # Safety
1346    ///
1347    /// As [`eval_with_handlers`]: R's main thread, inside a miniextendr
1348    /// boundary (a `#[miniextendr]` function body, a
1349    /// [`with_r_unwind_protect`](crate::unwind_protect::with_r_unwind_protect)
1350    /// closure, ...).
1351    pub unsafe fn reraise(self) -> ! {
1352        unsafe {
1353            let scope = ProtectScope::new();
1354            let cond = scope.protect_raw(without_try_catch_frame_call(self.condition()));
1355            // `stop(cond)` with the condition bound in a child of base, so a
1356            // traceback shows `stop(cond)` rather than the deparsed object.
1357            let env = scope.protect_raw(sys::R_NewEnv(R_BaseEnv, crate::Rboolean::FALSE, 1));
1358            let cond_sym = RSymbol::from_cstr(c"cond").as_sexp();
1359            REnv::from_sexp(env).define_symbol(cond_sym, cond);
1360            let call = scope.protect_raw(sys::Rf_lang2(
1361                RSymbol::from_cstr(c"stop").as_sexp(),
1362                cond_sym,
1363            ));
1364            eval_with_handlers(call, env);
1365        }
1366        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("stop() returned without signalling an error")));
}unreachable!("stop() returned without signalling an error")
1367    }
1368
1369    /// Take a caught condition: read its classes, message and call, and root
1370    /// the condition with its call.
1371    ///
1372    /// # Safety
1373    ///
1374    /// R's main thread; `cond` is rooted by the caller for the call.
1375    unsafe fn from_condition(cond: SEXP) -> Self {
1376        unsafe {
1377            let classes = string_vector(cond.get_class());
1378            let message = condition_message(cond);
1379            let call = OwnedProtect::new(condition_call(cond));
1380            Self::adopt(cond, call.get(), message, classes)
1381        }
1382    }
1383
1384    /// A `simpleError` with `message` and no call, for a failure that raised
1385    /// no R condition (a parse failure, a jump to the top level, a result
1386    /// [`RCall::eval_as`] can't read).
1387    ///
1388    /// # Safety
1389    ///
1390    /// R's main thread.
1391    unsafe fn simple_error(message: &str) -> Self {
1392        const CLASSES: [&str; 3] = ["simpleError", "error", "condition"];
1393        unsafe {
1394            let scope = ProtectScope::new();
1395            // list(message = <message>, call = NULL): `alloc_list` fills
1396            // `NULL`, and each fresh element is stored before the next
1397            // allocation.
1398            let cond = scope.protect_raw(SEXP::alloc_list(2));
1399            cond.set_vector_elt(0, SEXP::scalar_string_from_str(message));
1400            let names = scope.protect_raw(SEXP::alloc_strsxp(2));
1401            names.set_string_elt(0, SEXP::charsxp("message"));
1402            names.set_string_elt(1, SEXP::charsxp("call"));
1403            cond.set_names(names);
1404            let class = scope.protect_raw(SEXP::alloc_strsxp(3));
1405            for (i, name) in (0..).zip(CLASSES) {
1406                class.set_string_elt(i, SEXP::charsxp(name));
1407            }
1408            cond.set_class(class);
1409            Self::adopt(
1410                cond,
1411                SEXP::nil(),
1412                message.to_owned(),
1413                CLASSES.map(String::from).to_vec(),
1414            )
1415        }
1416    }
1417
1418    /// Root `cond` and `call` together for the value's lifetime.
1419    ///
1420    /// # Safety
1421    ///
1422    /// R's main thread; `cond` and `call` are rooted by the caller for the
1423    /// call.
1424    unsafe fn adopt(cond: SEXP, call: SEXP, message: String, classes: Vec<String>) -> Self {
1425        unsafe {
1426            let roots = crate::Preserved::new(SEXP::alloc_list(2));
1427            roots.set_vector_elt(0, cond);
1428            roots.set_vector_elt(1, call);
1429            REvalError {
1430                message,
1431                classes,
1432                roots,
1433            }
1434        }
1435    }
1436}
1437
1438impl std::fmt::Display for REvalError {
1439    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1440        f.write_str(&self.message)
1441    }
1442}
1443
1444impl std::fmt::Debug for REvalError {
1445    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1446        f.debug_struct("REvalError")
1447            .field("message", &self.message)
1448            .field("classes", &self.classes)
1449            .finish_non_exhaustive()
1450    }
1451}
1452
1453impl std::error::Error for REvalError {}
1454
1455/// Returned as a `#[miniextendr]` function's `Err`, the caught error is raised
1456/// again with its message and its [specific classes](REvalError::specific_classes),
1457/// ahead of the `rust_error` layers; the call is the function's own.
1458impl crate::condition::RConditionData for REvalError {
1459    fn message(&self) -> String {
1460        self.message.clone()
1461    }
1462
1463    fn class(&self) -> Vec<String> {
1464        self.specific_classes().to_vec()
1465    }
1466}
1467
1468/// The elements of a character vector (`NA` as `"NA"`); empty for anything
1469/// else. Does not allocate.
1470///
1471/// # Safety
1472///
1473/// R's main thread.
1474unsafe fn string_vector(x: SEXP) -> Vec<String> {
1475    if x.type_of() != crate::SEXPTYPE::STRSXP {
1476        return Vec::new();
1477    }
1478    (0..x.xlength())
1479        .map(|i| {
1480            // SAFETY: `x` is a STRSXP, so its elements are CHARSXPs.
1481            unsafe { crate::from_r::charsxp_to_string_lossy(x.string_elt(i)) }
1482                .unwrap_or_else(|| "NA".to_owned())
1483        })
1484        .collect()
1485}
1486
1487/// `conditionMessage(cond)`, through S3 dispatch, so a class with its own
1488/// method (rlang's errors, say) words its message. If the method fails or
1489/// returns no string: the condition's `message` field, then a placeholder.
1490///
1491/// # Safety
1492///
1493/// R's main thread; `cond` rooted.
1494unsafe fn condition_message(cond: SEXP) -> String {
1495    unsafe {
1496        let call = OwnedProtect::new(RCall::new("conditionMessage").quoted_arg(cond).build());
1497        if let Caught::Value(message) = try_eval_raw(call.get(), R_BaseEnv) {
1498            let lines = string_vector(message);
1499            if !lines.is_empty() {
1500                return lines.join("\n");
1501            }
1502        }
1503        if cond.type_of() == crate::SEXPTYPE::VECSXP {
1504            let names = string_vector(cond.get_names());
1505            if let Some(i) = names.iter().position(|name| name == "message") {
1506                let i = isize::try_from(i).expect("list index exceeds isize::MAX");
1507                let lines = string_vector(cond.vector_elt(i));
1508                if !lines.is_empty() {
1509                    return lines.join("\n");
1510                }
1511            }
1512        }
1513        "R error with no message".to_owned()
1514    }
1515}
1516
1517/// `conditionCall(cond)`, or `NULL` if the method fails or the call is the
1518/// `tryCatch()` frame's ([`is_try_catch_frame_call`]). Unprotected.
1519///
1520/// # Safety
1521///
1522/// R's main thread; `cond` rooted.
1523unsafe fn condition_call(cond: SEXP) -> SEXP {
1524    unsafe {
1525        let call = OwnedProtect::new(RCall::new("conditionCall").quoted_arg(cond).build());
1526        match try_eval_raw(call.get(), R_BaseEnv) {
1527            Caught::Value(call) if !is_try_catch_frame_call(call) => call,
1528            Caught::Value(_) | Caught::Error(_) | Caught::Exit => SEXP::nil(),
1529        }
1530    }
1531}
1532
1533/// Whether `call` is `doTryCatch(return(expr), name, parentenv, handler)`,
1534/// the frame of R's `tryCatch()`, which `R_tryCatchError` evaluates in. An
1535/// error raised at the top of the evaluated expression (`stop()` called
1536/// directly, not from a function) takes that frame's call as its own; it is
1537/// no call of the user's code, so [`REvalError::call`] reports `None` there.
1538/// Base R's `try()` recognises the frame by the same `doTryCatch` head.
1539fn is_try_catch_frame_call(call: SEXP) -> bool {
1540    call.type_of() == crate::SEXPTYPE::LANGSXP
1541        // SAFETY: `doTryCatch` is a literal without NUL; interning is main-thread work.
1542        && call.car() == unsafe { RSymbol::from_cstr(c"doTryCatch") }.as_sexp()
1543}
1544
1545/// `cond`, or a shallow copy of it whose `call` element is `NULL` when that
1546/// element is the [`tryCatch()` frame's call](is_try_catch_frame_call): the
1547/// condition [`REvalError::reraise`] signals, with the call
1548/// [`REvalError::call`] reports.
1549///
1550/// # Safety
1551///
1552/// R's main thread; `cond` rooted by the caller. The result is unprotected.
1553unsafe fn without_try_catch_frame_call(cond: SEXP) -> SEXP {
1554    unsafe {
1555        if cond.type_of() != crate::SEXPTYPE::VECSXP {
1556            return cond;
1557        }
1558        let names = string_vector(cond.get_names());
1559        let Some(i) = names.iter().position(|name| name == "call") else {
1560            return cond;
1561        };
1562        let i = isize::try_from(i).expect("list index exceeds isize::MAX");
1563        if !is_try_catch_frame_call(cond.vector_elt(i)) {
1564            return cond;
1565        }
1566        let copy = sys::Rf_shallow_duplicate(cond);
1567        copy.set_vector_elt(i, SEXP::nil());
1568        copy
1569    }
1570}
1571
1572// endregion
1573
1574// region: try_eval (evaluation with R errors caught)
1575
1576/// How an evaluation with R errors caught ended. The SEXPs are unprotected.
1577enum Caught {
1578    /// The expression's value.
1579    Value(SEXP),
1580    /// The `error` condition the evaluation raised.
1581    Error(SEXP),
1582    /// A jump to the top level without an error condition: an interrupt, an
1583    /// `abort` restart. Only [`try_eval_raw`] stops those.
1584    Exit,
1585}
1586
1587/// A C callback run with R errors caught, given the caller's data. An R error
1588/// jumps out of it, so it holds nothing to drop.
1589type TryBody = unsafe extern "C-unwind" fn(*mut c_void) -> SEXP;
1590
1591/// What [`catch_error`] shares with its callbacks.
1592struct TryEvalData {
1593    /// `R_tryCatchError`'s body: [`eval_body`] for an R expression.
1594    body: TryBody,
1595    body_data: *mut c_void,
1596    /// A protected list of one, which receives the value or the caught
1597    /// condition. The callbacks store into it without allocating, so what
1598    /// they hand back is rooted before R runs again.
1599    slot: SEXP,
1600    /// Whether the error handler ran.
1601    caught: bool,
1602}
1603
1604impl TryEvalData {
1605    /// The call of `body(body_data)`; `slot` is a protected list of one.
1606    fn new(body: TryBody, body_data: *mut c_void, slot: SEXP) -> Self {
1607        TryEvalData {
1608            body,
1609            body_data,
1610            slot,
1611            caught: false,
1612        }
1613    }
1614
1615    /// How [`catch_error`] ended, once it has returned. The SEXP is rooted by
1616    /// the slot.
1617    fn outcome(&self) -> Caught {
1618        let value = self.slot.vector_elt(0);
1619        if self.caught {
1620            Caught::Error(value)
1621        } else {
1622            Caught::Value(value)
1623        }
1624    }
1625}
1626
1627/// An R expression and the environment to evaluate it in: [`eval_body`]'s
1628/// data.
1629struct EvalArgs {
1630    expr: SEXP,
1631    env: SEXP,
1632}
1633
1634/// Evaluate an [`EvalArgs`]. An R error jumps from here to the `tryCatch()`
1635/// frame, over no Rust frame holding anything to drop.
1636unsafe extern "C-unwind" fn eval_body(data: *mut c_void) -> SEXP {
1637    let data = data.cast::<EvalArgs>();
1638    unsafe { sys::Rf_eval((*data).expr, (*data).env) }
1639}
1640
1641/// Run a [`TryEvalData`]'s body with `error` conditions caught
1642/// (`R_tryCatchError`), and store its value, or the caught condition, in its
1643/// slot. Returns `R_tryCatchError`'s value: the body's, or `NULL` after an
1644/// error ([`TryEvalData::outcome`] tells them apart).
1645///
1646/// `R_tryCatchError` runs R's `tryCatch()`, so the caller's calling handlers
1647/// see the evaluation's other conditions, and any jump that is not an error
1648/// (an exiting handler or restart of the caller's) passes through. A jump out
1649/// of here crosses no Rust frame holding anything to drop.
1650unsafe extern "C-unwind" fn catch_error(data: *mut c_void) -> SEXP {
1651    /// `R_tryCatchError`'s handler, run once R has unwound to the
1652    /// `tryCatch()` frame: keep the condition.
1653    unsafe extern "C-unwind" fn handler(cond: SEXP, data: *mut c_void) -> SEXP {
1654        let data = data.cast::<TryEvalData>();
1655        unsafe {
1656            (*data).slot.set_vector_elt(0, cond);
1657            (*data).caught = true;
1658        }
1659        SEXP::nil()
1660    }
1661
1662    let data = data.cast::<TryEvalData>();
1663    unsafe {
1664        let value = sys::R_tryCatchError(
1665            Some((*data).body),
1666            (*data).body_data,
1667            Some(handler),
1668            data.cast(),
1669        );
1670        if !(*data).caught {
1671            (*data).slot.set_vector_elt(0, value);
1672        }
1673        value
1674    }
1675}
1676
1677/// Call `body(body_data)`, hidden from the caller's handlers and restarts
1678/// (a new top-level context, `R_ToplevelExec`), with `error` conditions
1679/// caught ([`catch_error`]).
1680///
1681/// `R_tryCatchError` alone is not enough here: the caller's calling handlers
1682/// would see the evaluation's warnings, and an exiting handler or restart of
1683/// the caller would jump through the Rust frames. `R_ToplevelExec` gives the
1684/// isolation `R_tryEval` gives, and stops any jump that is not an error.
1685///
1686/// # Safety
1687///
1688/// R's main thread (the checked FFI routes a worker's call there);
1689/// `body(body_data)` is sound to call, and what it reads is rooted.
1690unsafe fn try_call_raw(body: TryBody, body_data: *mut c_void) -> Caught {
1691    /// `R_ToplevelExec`'s callback. A jump out of it crosses no Rust frame
1692    /// holding anything to drop.
1693    unsafe extern "C-unwind" fn toplevel(data: *mut c_void) {
1694        unsafe { catch_error(data) };
1695    }
1696
1697    unsafe {
1698        let slot = OwnedProtect::new(SEXP::alloc_list(1));
1699        let mut data = TryEvalData::new(body, body_data, slot.get());
1700        let completed = sys::R_ToplevelExec(Some(toplevel), (&raw mut data).cast());
1701        if completed == crate::sexp_types::Rboolean::FALSE {
1702            return Caught::Exit;
1703        }
1704        data.outcome()
1705    }
1706}
1707
1708/// Evaluate `expr` in `env` as [`try_call_raw`] calls a body.
1709///
1710/// # Safety
1711///
1712/// R's main thread (the checked FFI routes a worker's call there); `expr`
1713/// and `env` rooted.
1714unsafe fn try_eval_raw(expr: SEXP, env: SEXP) -> Caught {
1715    let mut args = EvalArgs { expr, env };
1716    unsafe { try_call_raw(eval_body, (&raw mut args).cast()) }
1717}
1718
1719/// Call `body(body_data)`, a C callback that may raise an R error, as
1720/// [`RCall::eval`] evaluates a call: hidden from the caller's handlers and
1721/// restarts, with an R error returned as `Err`. The value is unprotected.
1722///
1723/// For a C entry point that raises R errors, such as `R_WriteConnection`
1724/// (`connection/access.rs`); an R call needs no body of its own.
1725///
1726/// # Safety
1727///
1728/// As [`try_call_raw`]. An R error jumps out of `body`, so it must hold
1729/// nothing to drop.
1730#[cfg(all(feature = "connections", r_4_6))]
1731pub(crate) unsafe fn try_call(body: TryBody, body_data: *mut c_void) -> Result<SEXP, REvalError> {
1732    unsafe { into_result(try_call_raw(body, body_data)) }
1733}
1734
1735/// Evaluate `expr` in `env` in the caller's R context, with `error`
1736/// conditions caught ([`catch_error`]): the caller's handlers and restarts
1737/// stay in place, and any other R exit leaves as a Rust unwind
1738/// ([`call_unwinding`](crate::unwind_protect::call_unwinding)). Never
1739/// [`Caught::Exit`].
1740///
1741/// The slot is protected before the evaluation and holds the caught condition
1742/// until the caller roots it. An exit jumps to `R_UnwindProtect`'s frame,
1743/// which resets the protect stack to its level when the frame was entered, so
1744/// the slot's protection is still the top one when the unwind drops it.
1745///
1746/// # Safety
1747///
1748/// As [`try_eval_with_handlers`].
1749unsafe fn try_eval_unwinding(expr: SEXP, env: SEXP) -> Caught {
1750    unsafe {
1751        let slot = OwnedProtect::new(SEXP::alloc_list(1));
1752        let mut args = EvalArgs { expr, env };
1753        let mut data = TryEvalData::new(eval_body, (&raw mut args).cast(), slot.get());
1754        crate::unwind_protect::call_unwinding(catch_error, (&raw mut data).cast());
1755        data.outcome()
1756    }
1757}
1758
1759/// An evaluation's outcome with a caught error as an [`REvalError`]. The
1760/// value is unprotected.
1761///
1762/// # Safety
1763///
1764/// R's main thread, right after the evaluation: nothing has allocated since.
1765unsafe fn into_result(caught: Caught) -> Result<SEXP, REvalError> {
1766    unsafe {
1767        match caught {
1768            Caught::Value(value) => Ok(value),
1769            Caught::Error(cond) => {
1770                let cond = OwnedProtect::new(cond);
1771                Err(REvalError::from_condition(cond.get()))
1772            }
1773            Caught::Exit => Err(REvalError::simple_error(EXIT_MESSAGE)),
1774        }
1775    }
1776}
1777
1778/// [`try_eval_raw`] with a caught error as an [`REvalError`]. The value is
1779/// unprotected.
1780///
1781/// # Safety
1782///
1783/// As [`try_eval_raw`].
1784unsafe fn try_eval(expr: SEXP, env: SEXP) -> Result<SEXP, REvalError> {
1785    unsafe { into_result(try_eval_raw(expr, env)) }
1786}
1787
1788// endregion
1789
1790// region: Tests
1791
1792#[cfg(test)]
1793mod tests {
1794    use super::*;
1795
1796    // These tests verify compilation and basic invariants.
1797    // Full integration tests require the R runtime.
1798
1799    #[test]
1800    fn renv_types_are_sized() {
1801        // Just verify types compile and are sized
1802        fn assert_sized<T: Sized>() {}
1803        assert_sized::<RSymbol>();
1804        assert_sized::<RCall>();
1805        assert_sized::<REnv>();
1806    }
1807
1808    #[test]
1809    fn renv_constructors_compile() {
1810        // Verify all REnv constructor signatures compile.
1811        // Actual testing requires the R runtime.
1812        fn assert_env_fn<F: FnOnce() -> REnv>(_f: F) {}
1813        fn assert_env_result_fn<F: FnOnce() -> Result<REnv, REvalError>>(_f: F) {}
1814
1815        assert_env_fn(|| unsafe { REnv::global() });
1816        assert_env_fn(|| unsafe { REnv::base() });
1817        assert_env_fn(|| unsafe { REnv::empty() });
1818        assert_env_fn(REnv::base_namespace);
1819        assert_env_fn(|| unsafe { REnv::caller() });
1820        assert_env_result_fn(|| unsafe { REnv::package_namespace("base") });
1821    }
1822}
1823// endregion