Skip to main content

eval_with_handlers

Function eval_with_handlers 

Source
pub unsafe fn eval_with_handlers(expr: SEXP, env: SEXP) -> SEXP
Expand description

Evaluate expr in env in the caller’s R context, the way R code calling eval(expr, env) does.

RCall::eval, r_eval_streval_with_handlerstry_eval_with_handlers
R entry pointR_tryCatchError inside R_ToplevelExecRf_eval in its own R_UnwindProtectR_tryCatchError in its own R_UnwindProtect
caller’s withCallingHandlers(), suppressWarnings()not seen (R_ToplevelExec empties the handler and restart stacks)see every warning, message and conditionsee every warning, message and condition except an error
R errorErr(REvalError): R’s condition, with its message (no Error in prefix, no Calls: line), call and classes; the Rust code decides what to raiseunwinds as the R condition, class and call keptErr(REvalError), as from RCall::eval
caller’s tryCatch(warning = ), restarts, interruptsnot seen (an interrupt ends the evaluation with an Err)exit through the Rust framesexit through the Rust frames

An R error, or any other exit from the evaluation (an exiting handler of the caller’s tryCatch(), invokeRestart(), an interrupt), stops at this function’s own R_UnwindProtect frame, so it never jumps over a Rust frame. It then leaves as a Rust unwind: the frames between here and the #[miniextendr] boundary drop their values (a guard’s Drop runs), and the boundary hands the exit back to R, which carries on to the handler or restart it was going to, with the original condition object. A warning or message the caller muffles returns here and the evaluation continues.

So this is the evaluator for code a user wrote: an argument passed unevaluated (crate::Quoted, crate::Quosure), a callback, a call into a package whose conditions are part of its interface. Keep RCall::eval for internal calls whose failure the Rust code handles itself, for instance by raising its own condition with the caught message (REvalError::reraise_class). try_eval_with_handlers combines the two: the caller’s handlers see warnings and messages, and an R error comes back as Err for the Rust code to raise.

The unwind must reach a miniextendr boundary. Don’t catch it with std::panic::catch_unwind in between, or the R exit is abandoned (R carries on as after R_tryEval, without running the caller’s handler); resume a caught payload with std::panic::resume_unwind on the same thread before the call returns. An exit resumed in a later call has lost its target: the boundary raises an error instead. Don’t call this from a Drop implementation: an exit there would start an unwind during an unwind, which aborts.

§Safety

  • R’s main thread, inside a miniextendr boundary: a #[miniextendr] function body, a with_r_unwind_protect closure, an ALTREP callback with the r_unwind or rust_unwind guard (not unsafe, which catches nothing), a connection callback, a with_r_thread closure.
  • expr and env stay rooted for the call; env is an environment.

§Returns

The value, unprotected: protect it before the next allocation.