miniextendr_api/quoted.rs
1//! Unevaluated arguments: the [`Quoted`] and [`Quosure`] parameter markers.
2//!
3//! A generated R wrapper forces every argument it hands to `.Call()`, so a
4//! Rust body normally sees values only. A parameter typed `Quoted` or
5//! `Quosure` is passed unevaluated instead: the body receives the argument's
6//! expression and the environment to evaluate it in, and decides what to do
7//! with them. That is what `subset()`-style methods and APIs taking bare
8//! column names (`f(data, TIME)` as well as `f(data, "TIME")`) need.
9//!
10//! | marker | the wrapper passes | needs | `{{ x }}` / `!!x` |
11//! |---|---|---|---|
12//! | [`Quoted`] | `list(substitute(x), parent.frame())` | base R | no |
13//! | [`Quosure`] | `rlang::enquo(x)` | rlang in `Imports` | yes |
14//!
15//! ```ignore
16//! use miniextendr_api::{miniextendr, Quoted, SEXP};
17//!
18//! /// Rows of `data` where `cond` holds; `cond` sees the columns of `data`,
19//! /// then the caller's variables.
20//! #[miniextendr]
21//! pub fn keep_rows(data: SEXP, cond: Quoted) -> Vec<i32> {
22//! let keep = cond.eval_in(data);
23//! // ... read the logical vector
24//! # vec![]
25//! }
26//! ```
27//!
28//! ```r
29//! threshold <- 3
30//! keep_rows(df, value > threshold)
31//! ```
32//!
33//! The wrapper never forces the argument: a `Quoted` / `Quosure` parameter
34//! takes no R-side checks and none of the per-parameter options (`coerce`,
35//! `match_arg`, `choices`, `default`, `no_na`, `inherits`, ...), which are
36//! compile errors on it. A function taking one runs on R's main thread.
37//! It works on free functions, including standalone S3 methods
38//! (`#[miniextendr(s3(generic = "subset", class = "..."))]`): in a method
39//! reached through `UseMethod()`, `parent.frame()` is the generic's caller, as
40//! in base R's `subset.data.frame()`.
41//!
42//! A missing argument is a conversion error naming the parameter
43//! (`argument "cond" is missing, with no default`), like forcing it in R. Take
44//! `Missing<Quoted>` / `Missing<Quosure>` to make the argument optional.
45//!
46//! Evaluation goes through [`crate::expression::eval_with_handlers`]: the
47//! caller's `withCallingHandlers()` / `suppressWarnings()` see what the
48//! expression signals, and an error, or the exit of a `tryCatch()` handler set
49//! up around the call, unwinds through the Rust frames (running their
50//! destructors) and carries on in R as the original condition.
51//!
52//! Both markers borrow from the `.Call()` arguments, which R keeps rooted for
53//! the call; the lifetime keeps them from outliving it. They are `!Send`.
54//!
55//! A `Quosure` can also be read from a quosure found in an R object
56//! ([`Quosure::from_sexp`]), such as one a wrapper wrote into a call with
57//! `c(time = {{ t }})`, or built from an expression and an environment
58//! ([`Quosure::new`], [`Quosure::args`]). It then borrows the root the caller
59//! holds: the SEXP it was read from, or a [`ProtectScope`].
60
61use std::marker::PhantomData;
62
63use crate::expression::RCall;
64use crate::from_r::charsxp_to_str;
65use crate::gc_protect::{OwnedProtect, ProtectScope};
66use crate::missing::Missing;
67use crate::{PairListExt, SEXP, SEXPTYPE, SexpExt};
68
69/// The name an argument expression stands for: a symbol's name, or the value
70/// of a length-1 character literal. `None` for anything else, and for `NA`.
71fn expr_name<'a>(expr: SEXP) -> Option<&'a str> {
72 match expr.type_of() {
73 // Symbols are never collected; their print names live for the session.
74 SEXPTYPE::SYMSXP => {
75 let name = unsafe { charsxp_to_str(expr.printname()) };
76 (!name.is_empty()).then_some(name)
77 }
78 SEXPTYPE::STRSXP if expr.len() == 1 => {
79 let elt = expr.string_elt(0);
80 // The CHARSXP is held by `expr`, which the caller keeps rooted.
81 (!elt.is_na_string()).then(|| unsafe { charsxp_to_str(elt) })
82 }
83 _ => None,
84 }
85}
86
87// region: Quoted
88
89/// An argument passed unevaluated: its expression and the environment it was
90/// written in, as base R's `substitute(x)` and `parent.frame()` give them.
91///
92/// Take it as a `#[miniextendr]` parameter. The generated wrapper keeps the
93/// parameter as an R formal and passes
94/// `if (missing(x)) quote(expr=) else list(substitute(x), parent.frame())`,
95/// which never forces the argument. See the [module docs](self).
96///
97/// ```ignore
98/// use miniextendr_api::{miniextendr, Quoted, SEXP};
99///
100/// #[miniextendr]
101/// pub fn column_name(col: Quoted) -> Option<String> {
102/// col.as_name().map(str::to_owned) // column_name(TIME), column_name("TIME")
103/// }
104///
105/// #[miniextendr]
106/// pub fn evaluate(expr: Quoted) -> SEXP {
107/// expr.eval() // eval(expr, env)
108/// }
109/// ```
110///
111/// `parent.frame()` is the environment of the function that called the
112/// wrapper, which is where the expression was written when the user calls the
113/// wrapper directly or through `UseMethod()`. A wrapper reached through a
114/// function that forwards its own argument (`g <- function(y) f(y)`) receives
115/// `y` and `g`'s frame, as `subset()` does. To resolve forwarding, `{{ x }}`
116/// and `!!x`, take a [`Quosure`].
117pub struct Quoted<'a> {
118 expr: SEXP,
119 env: SEXP,
120 /// Borrows the `.Call()` argument that roots `expr` and `env`.
121 _arg: PhantomData<&'a SEXP>,
122 /// R objects stay on R's main thread.
123 _not_send: PhantomData<*const ()>,
124}
125
126impl<'a> Quoted<'a> {
127 /// Read the argument a generated wrapper passed for a `Quoted` parameter:
128 /// `None` when the argument is missing (`quote(expr=)`). Generated code
129 /// calls this; user code has no reason to.
130 ///
131 /// # Panics
132 ///
133 /// When `arg` is not the wrapper's `list(<expr>, <environment>)`.
134 ///
135 /// # Safety
136 ///
137 /// R's main thread; `arg` stays rooted for `'a`.
138 #[doc(hidden)]
139 pub unsafe fn from_wrapper_arg(arg: &'a SEXP) -> Option<Self> {
140 let arg = *arg;
141 if arg == SEXP::missing_arg() {
142 return None;
143 }
144 assert!(
145 arg.type_of() == SEXPTYPE::VECSXP
146 && arg.len() == 2
147 && arg.vector_elt(1).is_environment(),
148 "a `Quoted` argument is passed as `list(substitute(x), parent.frame())`, got {}",
149 arg.type_of().type_name(),
150 );
151 Some(Quoted {
152 expr: arg.vector_elt(0),
153 env: arg.vector_elt(1),
154 _arg: PhantomData,
155 _not_send: PhantomData,
156 })
157 }
158
159 /// [`Self::from_wrapper_arg`] for a `Missing<Quoted>` parameter.
160 ///
161 /// # Safety
162 ///
163 /// As [`Self::from_wrapper_arg`].
164 #[doc(hidden)]
165 pub unsafe fn missing_from_wrapper_arg(arg: &'a SEXP) -> Missing<Self> {
166 match unsafe { Self::from_wrapper_arg(arg) } {
167 Some(quoted) => Missing::Present(quoted),
168 None => Missing::Absent,
169 }
170 }
171
172 /// The expression as written: a symbol, a call, or a constant
173 /// (`substitute(x)`).
174 #[inline]
175 pub fn expr(&self) -> SEXP {
176 self.expr
177 }
178
179 /// The environment the expression was written in (`parent.frame()`).
180 #[inline]
181 pub fn env(&self) -> SEXP {
182 self.env
183 }
184
185 /// The name the argument stands for: `TIME` and `"TIME"` both give
186 /// `"TIME"`. `None` for any other expression (a call, a number, `NA`).
187 #[inline]
188 pub fn as_name(&self) -> Option<&'a str> {
189 expr_name(self.expr)
190 }
191
192 /// Evaluate the expression in its environment, `eval(expr, env)`, keeping
193 /// the caller's condition handlers
194 /// ([`eval_with_handlers`](crate::expression::eval_with_handlers)).
195 ///
196 /// The value is unprotected: protect it before the next allocation.
197 pub fn eval(&self) -> SEXP {
198 // SAFETY: a `Quoted` exists only inside the `#[miniextendr]` call that
199 // received it, on R's main thread; its argument roots both SEXPs.
200 unsafe { crate::expression::eval_with_handlers(self.expr, self.env) }
201 }
202
203 /// Evaluate the expression with `data` as a data mask, like
204 /// `eval(expr, data, env)`: the elements of a list or the columns of a
205 /// data frame are visible by name, ahead of the expression's environment.
206 /// The first of two elements with one name wins, and unnamed elements are
207 /// not visible. An environment is used as is (`env` is not consulted),
208 /// and `NULL` evaluates in the expression's environment.
209 ///
210 /// The value is unprotected: protect it before the next allocation.
211 ///
212 /// # Panics
213 ///
214 /// When `data` is not a list, a data frame, an environment or `NULL`.
215 pub fn eval_in(&self, data: SEXP) -> SEXP {
216 // SAFETY: as in `eval`; `data` is rooted by the caller.
217 unsafe { self.eval_masked(data, crate::expression::eval_with_handlers) }
218 }
219
220 /// [`eval_in`](Self::eval_in) with an R error returned as `Err`, as
221 /// [`try_eval_with_handlers`](crate::expression::try_eval_with_handlers)
222 /// returns it: the caller's handlers still see the expression's warnings
223 /// and messages. The mask and the rules for `data` are `eval_in`'s.
224 ///
225 /// A `subset()` method can explain a column that doesn't exist and leave
226 /// every other error as R raised it
227 /// ([`REvalError::reraise`](crate::expression::REvalError::reraise)):
228 ///
229 /// ```ignore
230 /// let keep = match subset.try_eval_in(table) {
231 /// Ok(keep) => keep,
232 /// // `unknown_column` compares the expression's names with the table's.
233 /// Err(e) if unknown_column(&subset, table) => {
234 /// rust_error!(class = "pkg_unknown_column", "{e}; the columns are ...")
235 /// }
236 /// // SAFETY: inside the `#[miniextendr]` call.
237 /// Err(e) => unsafe { e.reraise() },
238 /// };
239 /// ```
240 ///
241 /// The value is unprotected: protect it before the next allocation.
242 ///
243 /// # Panics
244 ///
245 /// When `data` is not a list, a data frame, an environment or `NULL`.
246 pub fn try_eval_in(&self, data: SEXP) -> Result<SEXP, crate::expression::REvalError> {
247 // SAFETY: as in `eval`; `data` is rooted by the caller.
248 unsafe { self.eval_masked(data, crate::expression::try_eval_with_handlers) }
249 }
250
251 /// Evaluate the expression with `data` as a mask, as
252 /// [`eval_in`](Self::eval_in) describes, through `eval`.
253 ///
254 /// # Safety
255 ///
256 /// As `eval`'s: R's main thread, inside a miniextendr boundary; `data`
257 /// rooted by the caller.
258 unsafe fn eval_masked<R>(&self, data: SEXP, eval: unsafe fn(SEXP, SEXP) -> R) -> R {
259 unsafe {
260 match data.type_of() {
261 SEXPTYPE::ENVSXP => eval(self.expr, data),
262 SEXPTYPE::NILSXP => eval(self.expr, self.env),
263 SEXPTYPE::VECSXP => {
264 // Rooted ahead of the evaluation's own protect frame, so an
265 // R exit leaves the protect stack as this guard expects.
266 let mask = OwnedProtect::new(data_mask(data, self.env));
267 eval(self.expr, mask.get())
268 }
269 other => panic!(
270 "`data` must be a list, a data frame, an environment or NULL, not {}",
271 other.type_name()
272 ),
273 }
274 }
275 }
276}
277
278/// A new environment enclosed by `enclos` that binds each named element of
279/// the list `data`, as `eval()` builds one for a list `envir`. Bound in
280/// reverse so the first of two same-named elements wins, as in `eval()`.
281///
282/// # Safety
283///
284/// R's main thread, inside a miniextendr boundary; `data` a rooted list,
285/// `enclos` an environment. The result is unprotected.
286unsafe fn data_mask(data: SEXP, enclos: SEXP) -> SEXP {
287 unsafe {
288 let n = data.len();
289 let size = i32::try_from(n).unwrap_or(i32::MAX);
290 let mask = OwnedProtect::new(crate::sys::R_NewEnv(enclos, crate::Rboolean::TRUE, size));
291 let env = crate::expression::REnv::from_sexp(mask.get());
292 let names = data.get_names();
293 if names.type_of() == SEXPTYPE::STRSXP {
294 for i in (0..n).rev() {
295 let i = isize::try_from(i).expect("list index fits in isize");
296 let name = names.string_elt(i);
297 if name.is_na_string() || name.len() == 0 {
298 continue;
299 }
300 // `names` is held by `data`; the symbol table keeps the symbol.
301 env.define_symbol(SEXP::install_char(name), data.vector_elt(i));
302 }
303 }
304 mask.get()
305 }
306}
307
308// endregion
309
310// region: Quosure
311
312/// Whether `x` is an rlang quosure: a one-sided formula, `~expr`, of class
313/// `quosure`.
314fn is_quosure(x: SEXP) -> bool {
315 x.is_language() && x.inherits_class(c"quosure")
316}
317
318/// The quosure `x` stands for: `x` itself, or, when its expression is a
319/// quosure too (`rlang::new_quosure(<quosure>)`), the innermost one. `None`
320/// when `x` is not a quosure.
321fn innermost_quosure(x: SEXP) -> Option<SEXP> {
322 if !is_quosure(x) {
323 return None;
324 }
325 let mut quo = x;
326 while is_quosure(quo.cdr().car()) {
327 quo = quo.cdr().car();
328 }
329 Some(quo)
330}
331
332/// The function `rlang::new_quosure`, unprotected.
333///
334/// # Panics
335///
336/// When rlang cannot be loaded.
337///
338/// # Safety
339///
340/// R's main thread.
341unsafe fn rlang_new_quosure() -> SEXP {
342 use crate::sys::{R_BaseEnv, Rf_install};
343 unsafe {
344 // `rlang::new_quosure`; `::` takes both names unevaluated.
345 RCall::new("::")
346 .arg(Rf_install(c"rlang".as_ptr()))
347 .arg(Rf_install(c"new_quosure".as_ptr()))
348 .eval(R_BaseEnv)
349 .unwrap_or_else(|e| {
350 panic!(
351 "a `Quosure` of a bare expression is built with `rlang::new_quosure()`, \
352 which needs the rlang package: {e}"
353 )
354 })
355 }
356}
357
358/// `new_quosure(quote(<expr>), env)`, unprotected.
359///
360/// # Safety
361///
362/// R's main thread; `new_quosure` (from [`rlang_new_quosure`]), `expr` and
363/// `env` rooted, `env` an environment.
364unsafe fn wrap_in_quosure(new_quosure: SEXP, expr: SEXP, env: SEXP) -> SEXP {
365 unsafe {
366 RCall::from_sexp(new_quosure)
367 .quoted_arg(expr)
368 .arg(env)
369 .eval(crate::sys::R_BaseEnv)
370 .unwrap_or_else(|e| panic!("`rlang::new_quosure()` failed: {e}"))
371 }
372}
373
374/// An argument captured as an rlang quosure, `rlang::enquo(x)`: its expression
375/// and environment, with rlang's argument forwarding and injection resolved.
376///
377/// Take it as a `#[miniextendr]` parameter. The generated wrapper keeps the
378/// parameter as an R formal and passes
379/// `if (missing(x)) quote(expr=) else rlang::enquo(x)`, which never forces the
380/// argument. Unlike [`Quoted`], a wrapper reached through
381/// `function(col) f(data, {{ col }})` or called as `f(data, !!sym)` receives
382/// the expression the user wrote, in the user's environment.
383///
384/// **The package must list rlang in `Imports`.** The macro cannot see
385/// `DESCRIPTION`; without rlang the wrapper fails when it is called.
386///
387/// The expression and environment are read from the quosure directly (a
388/// quosure is a one-sided formula, `~expr`, of class `quosure` whose
389/// environment is its `.Environment` attribute), so [`Self::expr`],
390/// [`Self::env`] and [`Self::as_name`] call no R code.
391/// [`Self::eval_tidy`] evaluates through `rlang::eval_tidy()`, which resolves
392/// nested quosures and the `.data` / `.env` pronouns. Hand [`Self::sexp`] to
393/// other quosure-aware R functions (`tidyselect::eval_select()`) with
394/// [`RCall::eval_with_handlers`](crate::expression::RCall::eval_with_handlers).
395///
396/// An argument holding several selections, a role map such as
397/// `cols = c(time = TAD, dose = NA)`, reads entry by entry with
398/// [`Self::args`]: each argument of the call as a quosure of its own. A
399/// quosure found in an R object reads with [`Self::from_sexp`], and
400/// [`Self::new`] makes one from an expression and an environment.
401pub struct Quosure<'a> {
402 quo: SEXP,
403 expr: SEXP,
404 env: SEXP,
405 /// Borrows the root that keeps the quosure alive: the `.Call()` argument,
406 /// the SEXP it was read from, or a [`ProtectScope`].
407 _arg: PhantomData<&'a SEXP>,
408 /// R objects stay on R's main thread.
409 _not_send: PhantomData<*const ()>,
410}
411
412impl<'a> Quosure<'a> {
413 /// Read a quosure's expression and environment.
414 ///
415 /// # Safety
416 ///
417 /// R's main thread; `quo` is a quosure, rooted for `'a`.
418 unsafe fn read(quo: SEXP) -> Self {
419 let expr = quo.cdr().car();
420 let env = quo.get_attr(unsafe { crate::sys::Rf_install(c".Environment".as_ptr()) });
421 Quosure {
422 quo,
423 expr,
424 env,
425 _arg: PhantomData,
426 _not_send: PhantomData,
427 }
428 }
429
430 /// Read the argument a generated wrapper passed for a `Quosure` parameter:
431 /// `None` when the argument is missing (`quote(expr=)`, or a quosure of
432 /// the missing argument, which `{{ x }}` gives for a missing `x`).
433 /// Generated code calls this; user code has no reason to.
434 ///
435 /// # Panics
436 ///
437 /// When `arg` is not a quosure.
438 ///
439 /// # Safety
440 ///
441 /// R's main thread; `arg` stays rooted for `'a`.
442 #[doc(hidden)]
443 pub unsafe fn from_wrapper_arg(arg: &'a SEXP) -> Option<Self> {
444 let quo = *arg;
445 if quo == SEXP::missing_arg() {
446 return None;
447 }
448 assert!(
449 is_quosure(quo),
450 "a `Quosure` argument is passed as `rlang::enquo(x)`, got {}",
451 quo.type_of().type_name(),
452 );
453 let quosure = unsafe { Self::read(quo) };
454 (quosure.expr != SEXP::missing_arg()).then_some(quosure)
455 }
456
457 /// [`Self::from_wrapper_arg`] for a `Missing<Quosure>` parameter.
458 ///
459 /// # Safety
460 ///
461 /// As [`Self::from_wrapper_arg`].
462 #[doc(hidden)]
463 pub unsafe fn missing_from_wrapper_arg(arg: &'a SEXP) -> Missing<Self> {
464 match unsafe { Self::from_wrapper_arg(arg) } {
465 Some(quosure) => Missing::Present(quosure),
466 None => Missing::Absent,
467 }
468 }
469
470 /// Read a quosure the caller holds: `None` when `sexp` is not one.
471 ///
472 /// A quosure whose expression is a quosure too, which
473 /// `rlang::new_quosure(<quosure>)` makes, is looked through: the result is
474 /// the innermost one, which is what the outer one evaluates to and what
475 /// `tidyselect::eval_select()` accepts (it refuses the outer one). A
476 /// quosure of the missing argument is read as it is: [`Self::expr`] is
477 /// [`SEXP::missing_arg()`].
478 ///
479 /// ```ignore
480 /// // `call` is the expression of a `Quosure` argument,
481 /// // `c(time = {{ t }})` written by a forwarding wrapper.
482 /// for (name, arg) in call.args() {
483 /// if let Some(quosure) = unsafe { Quosure::from_sexp(&arg) } {
484 /// // the quosure `{{ t }}` left in the call, as it is
485 /// }
486 /// }
487 /// ```
488 ///
489 /// # Safety
490 ///
491 /// R's main thread, inside a miniextendr boundary (a `#[miniextendr]`
492 /// body, a `with_r_unwind_protect` closure; [`Self::eval_tidy`] needs
493 /// one); `sexp` stays rooted for `'a`.
494 pub unsafe fn from_sexp(sexp: &'a SEXP) -> Option<Self> {
495 innermost_quosure(*sexp).map(|quo| unsafe { Self::read(quo) })
496 }
497
498 /// A quosure of `expr` in `env`, rooted by `scope`.
499 ///
500 /// A bare expression is wrapped with `rlang::new_quosure(expr, env)`. A
501 /// quosure is returned as it is, through [`Self::from_sexp`] (`env` is
502 /// not used): wrapping a quosure again makes a formula that
503 /// `tidyselect::eval_select()` refuses. So an argument read from a call
504 /// can go through `new()` whether or not a wrapper wrote a quosure there.
505 /// The missing argument (an empty argument, `c(a = )`) is wrapped too:
506 /// [`Self::expr`] is then [`SEXP::missing_arg()`].
507 ///
508 /// Each call protects one value in `scope`.
509 ///
510 /// # Panics
511 ///
512 /// When `env` is not an environment, or when `expr` is not a quosure and
513 /// rlang cannot be loaded (a package taking a [`Quosure`] lists rlang in
514 /// `Imports`).
515 ///
516 /// # Safety
517 ///
518 /// As [`Self::from_sexp`]; `expr` and `env` are rooted by the caller for
519 /// the call.
520 pub unsafe fn new(expr: SEXP, env: SEXP, scope: &'a ProtectScope) -> Self {
521 assert!(
522 env.is_environment(),
523 "`Quosure::new()` takes an environment as `env`, not {}",
524 env.type_of().type_name(),
525 );
526 let quo = match innermost_quosure(expr) {
527 Some(quo) => quo,
528 // The function's guard drops before `scope` protects the quosure,
529 // so the protect stack unwinds in order; nothing allocates between.
530 None => unsafe {
531 let new_quosure = OwnedProtect::new(rlang_new_quosure());
532 wrap_in_quosure(new_quosure.get(), expr, env)
533 },
534 };
535 unsafe { Self::read(scope.protect_raw(quo)) }
536 }
537
538 /// The arguments of the quosure's call, each as a quosure with its name,
539 /// rooted by `scope`; `None` when the expression is not a call.
540 ///
541 /// `c(time = TAD, dose = NA)` gives `(Some("time"), TAD)` and
542 /// `(Some("dose"), NA)`, each in this quosure's environment. An argument
543 /// a wrapper wrote as a quosure (`c(time = {{ t }})`) comes back as it is,
544 /// in its own environment, as [`Self::new`] returns it; any other is
545 /// wrapped in this quosure's environment with `rlang::new_quosure()`. The
546 /// name is `None` for an unnamed argument; the function (`c`) is not one
547 /// of the arguments.
548 ///
549 /// ```ignore
550 /// #[miniextendr]
551 /// pub fn roles(data: SEXP, cols: Quosure) -> Vec<String> {
552 /// let scope = unsafe { ProtectScope::new() };
553 /// let Some(args) = cols.args(&scope) else {
554 /// panic!("`cols` must be a call, `c(role = column, ...)`");
555 /// };
556 /// for (role, quosure) in args {
557 /// // `quosure.expr()` is `NA` / `NULL` as written; hand the others
558 /// // to `tidyselect::eval_select()`.
559 /// }
560 /// # vec![]
561 /// }
562 /// ```
563 ///
564 /// All the quosures are held in one list, which `scope` protects.
565 ///
566 /// # Panics
567 ///
568 /// As [`Self::new`]: when an argument is not a quosure and rlang cannot
569 /// be loaded.
570 pub fn args<'s>(
571 &'s self,
572 scope: &'s ProtectScope,
573 ) -> Option<Vec<(Option<&'s str>, Quosure<'s>)>> {
574 if !self.expr.is_language() {
575 return None;
576 }
577 let args: Vec<(Option<&'s str>, SEXP)> = self.expr.args().collect();
578 // SAFETY: a `Quosure` exists only on R's main thread, rooted for
579 // `'a`: so are its call and the arguments in it. Each quosure is
580 // stored in `held` as it is made, and `held` is protected by `scope`
581 // before the function's guard, which drops first.
582 unsafe {
583 let held = scope.alloc_vecsxp(args.len()).into_raw();
584 let mut new_quosure: Option<OwnedProtect> = None;
585 for (i, &(_, arg)) in args.iter().enumerate() {
586 let quo = match innermost_quosure(arg) {
587 Some(quo) => quo,
588 None => {
589 let new_quosure = new_quosure
590 .get_or_insert_with(|| OwnedProtect::new(rlang_new_quosure()));
591 wrap_in_quosure(new_quosure.get(), arg, self.env)
592 }
593 };
594 held.set_vector_elt(r_index(i), quo);
595 }
596 drop(new_quosure);
597 Some(
598 args.into_iter()
599 .enumerate()
600 .map(|(i, (name, _))| (name, Quosure::read(held.vector_elt(r_index(i)))))
601 .collect(),
602 )
603 }
604 }
605
606 /// The quosure itself, for R functions that take one
607 /// (`rlang::eval_tidy()`, `tidyselect::eval_select()`).
608 #[inline]
609 pub fn sexp(&self) -> SEXP {
610 self.quo
611 }
612
613 /// The quosure's expression (`rlang::quo_get_expr()`).
614 #[inline]
615 pub fn expr(&self) -> SEXP {
616 self.expr
617 }
618
619 /// The quosure's environment (`rlang::quo_get_env()`): the empty
620 /// environment for a constant.
621 #[inline]
622 pub fn env(&self) -> SEXP {
623 self.env
624 }
625
626 /// The name the argument stands for: `TIME`, `"TIME"`, `{{ col }}` with
627 /// `col = TIME` and `!!rlang::sym("TIME")` all give `"TIME"`. A quosure
628 /// nested in the expression is looked through. `None` for any other
629 /// expression.
630 pub fn as_name(&self) -> Option<&'a str> {
631 let mut expr = self.expr;
632 while is_quosure(expr) {
633 expr = expr.cdr().car();
634 }
635 expr_name(expr)
636 }
637
638 /// Evaluate the quosure with `data` as a data mask,
639 /// `rlang::eval_tidy(quo, data)`: columns of `data` win over variables of
640 /// the same name in the quosure's environment, and `.data$x` / `.env$x`
641 /// pick one side. `data` may be `NULL`.
642 ///
643 /// Evaluated with
644 /// [`eval_with_handlers`](crate::expression::eval_with_handlers), so the
645 /// caller's handlers see what the expression signals.
646 ///
647 /// The value is unprotected: protect it before the next allocation.
648 pub fn eval_tidy(&self, data: SEXP) -> SEXP {
649 // SAFETY: a `Quosure` exists only inside a miniextendr boundary on
650 // R's main thread (the `#[miniextendr]` call that received it, or
651 // the caller of `from_sexp` / `new`); `data` is rooted by the caller.
652 unsafe { self.eval_tidy_with(data, crate::expression::eval_with_handlers) }
653 }
654
655 /// [`eval_tidy`](Self::eval_tidy) with an R error returned as `Err`, as
656 /// [`try_eval_with_handlers`](crate::expression::try_eval_with_handlers)
657 /// returns it: the caller's handlers still see the quosure's warnings and
658 /// messages. A missing rlang is an `Err` too.
659 ///
660 /// The value is unprotected: protect it before the next allocation.
661 pub fn try_eval_tidy(&self, data: SEXP) -> Result<SEXP, crate::expression::REvalError> {
662 // SAFETY: as in `eval_tidy`.
663 unsafe { self.eval_tidy_with(data, crate::expression::try_eval_with_handlers) }
664 }
665
666 /// `rlang::eval_tidy(quo, data)` evaluated by `eval`.
667 ///
668 /// # Safety
669 ///
670 /// As `eval_tidy`'s: R's main thread, inside a miniextendr boundary;
671 /// `data` rooted by the caller.
672 unsafe fn eval_tidy_with<R>(&self, data: SEXP, eval: unsafe fn(SEXP, SEXP) -> R) -> R {
673 use crate::sys::{R_BaseEnv, Rf_install, Rf_lang2, Rf_lang3};
674 unsafe {
675 let scope = ProtectScope::new();
676 // `rlang::eval_tidy(quote(<quo>), quote(<data>))`; a missing rlang raises
677 // R's own error, through the caller's handlers.
678 let fun = scope.protect_raw(Rf_lang3(
679 Rf_install(c"::".as_ptr()),
680 Rf_install(c"rlang".as_ptr()),
681 Rf_install(c"eval_tidy".as_ptr()),
682 ));
683 // Both arguments pass through `quote()`, so neither the quosure
684 // nor a language `data` is evaluated on the way in.
685 let quote = Rf_install(c"quote".as_ptr());
686 let quoted = scope.protect_raw(Rf_lang2(quote, self.quo));
687 let data = scope.protect_raw(Rf_lang2(quote, data));
688 let call = scope.protect_raw(Rf_lang3(fun, quoted, data));
689 eval(call, R_BaseEnv)
690 }
691 }
692}
693
694/// An R vector index.
695fn r_index(i: usize) -> isize {
696 isize::try_from(i).expect("an R vector index fits in isize")
697}
698
699// endregion
700
701#[cfg(test)]
702mod tests {
703 use super::*;
704
705 #[test]
706 fn markers_hold_their_sexps_only() {
707 assert_eq!(
708 std::mem::size_of::<Quoted<'static>>(),
709 2 * std::mem::size_of::<SEXP>()
710 );
711 assert_eq!(
712 std::mem::size_of::<Quosure<'static>>(),
713 3 * std::mem::size_of::<SEXP>()
714 );
715 }
716}