1//! Condition-call markers: [`Call`] and [`CallerCall`].
2//!
3//! Every generated R wrapper hands its C entry point a call object in a hidden
4//! first slot, `.Call(C_pkg_f, .call = <call>, ...)`. Conditions raised from
5//! Rust (a panic, an `Err`, `error!()` & co.) and, for `caller` attribution,
6//! the wrapper's own R-side checks are attributed to that call, so
7//! `conditionCall()` names the function as the user wrote it. Which call it is
8//! comes from the wrapper's *attribution*:
9//!
10//! | attribution | `.call =` | names |
11//! |-------------|---------------------------|-----------------------------------------|
12//! | `wrapper` | `sys.call()` | the wrapper's own call (the default) |
13//! | `caller` | `.mx_call` (caller frame) | the hand-written R function delegating |
14//!
15//! A `#[miniextendr]` function chooses its attribution in one of three
16//! equivalent spellings, most specific first: a **marker parameter** on the
17//! signature, the **`call = wrapper | caller`** attribute, or the
18//! crate-wide default in `Cargo.toml`
19//! (`[package.metadata.miniextendr] call_attribution = "..."`). The framework
20//! default is `wrapper`.
21//!
22//! ```ignore
23//! use miniextendr_api::{Call, CallerCall, miniextendr};
24//!
25//! /// `.call = sys.call()`; `call` is that call, available to the body.
26//! #[miniextendr]
27//! pub fn scale(x: f64, call: Call) -> f64 { let _ = call.sexp(); x * 2.0 }
28//!
29//! /// Internal entry point behind a hand-written `scale2()` in R/: the caller's
30//! /// call, as written, reaches Rust as `call`.
31//! #[miniextendr(noexport)]
32//! pub fn scale2_impl(x: f64, _call: CallerCall) -> f64 { x * 2.0 }
33//! ```
34//!
35//! The marker parameter is not an R argument: the R wrapper's formals are the
36//! other parameters, and the C wrapper binds the marker from its hidden call
37//! slot. `Call` selects `wrapper` attribution, `CallerCall` selects `caller`
38//! (and, like the attribute, needs `noexport` or `internal`: an exported
39//! function's caller is arbitrary user code). A `caller` wrapper, however
40//! spelled, takes one extra R formal, `.call = NULL`: a hand-written helper
41//! between the public function and the entry point passes its caller's frame
42//! there (`call = parent.frame()`) or a call object, and the body receives the
43//! call that resolves to. `#[miniextendr(call_arg)]` gives a `wrapper`
44//! function, exported or not, the same formal, whose `NULL` is the wrapper's
45//! own call: an R function composing it (through `do.call()`, say) passes
46//! `.call = environment()` or a call object, and a `Call` parameter receives
47//! the call that resolves to. A marker and a `call = ...` attribute that disagree
48//! are a compile error, as are two markers on one
49//! function, a marker on an `extern "C-unwind"` function (no generated call
50//! slot) and a marker on a class method (methods keep the wrapper's own call).
51//! A function taking a marker runs on R's main thread, like one taking `SEXP`.
52//!
53//! Both types are transparent newtypes over [`SEXP`]: the language object of
54//! the call as written (`is.call()`). They are handles for inspection,
55//! `deparse()` in a log line say, never roots: the object lives only as long
56//! as the `.Call()` frame that carried it, so it must not outlive the function
57//! call it was passed to.
5859use crate::SEXP;
6061/// The call this wrapper attributes conditions to: the call as the user wrote
62/// it (`sys.call()` in the generated wrapper), or under `call_arg` the call
63/// passed as `.call`. Taking it selects `wrapper` attribution. See the
64/// [module docs](self).
65#[repr(transparent)]
66#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Call {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Call", &&self.0)
}
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Call { }
#[automatically_derived]
impl ::core::clone::Clone for Call {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<SEXP>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Call { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for Call { }
#[automatically_derived]
impl ::core::cmp::PartialEq for Call {
#[inline]
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for Call {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<SEXP>;
}
}Eq)]
67pub struct Call(SEXP);
6869/// The call of the R function that called this wrapper, as written
70/// (`.miniextendr_caller_call(.call)` in the generated wrapper), or the call a
71/// helper passed as the wrapper's `.call` formal.
72/// Taking it selects `caller` attribution; the function must be `noexport` or
73/// `internal`. See the [module docs](self).
74#[repr(transparent)]
75#[derive(#[automatically_derived]
impl ::core::fmt::Debug for CallerCall {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "CallerCall",
&&self.0)
}
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for CallerCall { }
#[automatically_derived]
impl ::core::clone::Clone for CallerCall {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<SEXP>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CallerCall { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for CallerCall { }
#[automatically_derived]
impl ::core::cmp::PartialEq for CallerCall {
#[inline]
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for CallerCall {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<SEXP>;
}
}Eq)]
76pub struct CallerCall(SEXP);
7778macro_rules!call_marker_impls {
79 ($name:ident) => {
80impl $name {
81/// Wrap the call object a generated C wrapper received in its
82 /// hidden `.call` slot. Generated code calls this; user code has no
83 /// reason to.
84#[doc(hidden)]
85 #[inline]
86pub const fn from_sexp(call: SEXP) -> Self {
87$name(call)
88 }
8990/// The call as an R language object.
91#[inline]
92pub const fn sexp(self) -> SEXP {
93self.0
94}
95 }
9697impl ::std::ops::Deref for $name {
98type Target = SEXP;
99100#[inline]
101fn deref(&self) -> &SEXP {
102&self.0
103}
104 }
105106impl From<$name> for SEXP {
107#[inline]
108fn from(call: $name) -> SEXP {
109 call.0
110}
111 }
112 };
113}
114115impl Call {
/// Wrap the call object a generated C wrapper received in its
/// hidden `.call` slot. Generated code calls this; user code has no
/// reason to.
#[doc(hidden)]
#[inline]
pub const fn from_sexp(call: SEXP) -> Self { Call(call) }
/// The call as an R language object.
#[inline]
pub const fn sexp(self) -> SEXP { self.0 }
}
impl ::std::ops::Deref for Call {
type Target = SEXP;
#[inline]
fn deref(&self) -> &SEXP { &self.0 }
}
impl From<Call> for SEXP {
#[inline]
fn from(call: Call) -> SEXP { call.0 }
}call_marker_impls!(Call);
116impl CallerCall {
/// Wrap the call object a generated C wrapper received in its
/// hidden `.call` slot. Generated code calls this; user code has no
/// reason to.
#[doc(hidden)]
#[inline]
pub const fn from_sexp(call: SEXP) -> Self { CallerCall(call) }
/// The call as an R language object.
#[inline]
pub const fn sexp(self) -> SEXP { self.0 }
}
impl ::std::ops::Deref for CallerCall {
type Target = SEXP;
#[inline]
fn deref(&self) -> &SEXP { &self.0 }
}
impl From<CallerCall> for SEXP {
#[inline]
fn from(call: CallerCall) -> SEXP { call.0 }
}call_marker_impls!(CallerCall);
117118/// A caller's call is a call: hand it to an API that takes [`Call`].
119impl From<CallerCall> for Call {
120#[inline]
121fn from(call: CallerCall) -> Call {
122Call(call.0)
123 }
124}
125126#[cfg(test)]
127mod tests {
128use super::*;
129130#[test]
131fn markers_are_transparent_over_sexp() {
132assert_eq!(std::mem::size_of::<Call>(), std::mem::size_of::<SEXP>());
133assert_eq!(
134 std::mem::size_of::<CallerCall>(),
135 std::mem::size_of::<SEXP>()
136 );
137let raw = SEXP::null();
138let call = Call::from_sexp(raw);
139assert_eq!(call.sexp(), raw);
140assert_eq!(SEXP::from(call), raw);
141let caller = CallerCall::from_sexp(raw);
142assert_eq!(Call::from(caller), call);
143assert_eq!(*caller, raw);
144 }
145}