Skip to main content

miniextendr_api/
call_marker.rs

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.
58
59use crate::SEXP;
60
61/// 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);
68
69/// 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);
77
78macro_rules! call_marker_impls {
79    ($name:ident) => {
80        impl $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]
86            pub const fn from_sexp(call: SEXP) -> Self {
87                $name(call)
88            }
89
90            /// The call as an R language object.
91            #[inline]
92            pub const fn sexp(self) -> SEXP {
93                self.0
94            }
95        }
96
97        impl ::std::ops::Deref for $name {
98            type Target = SEXP;
99
100            #[inline]
101            fn deref(&self) -> &SEXP {
102                &self.0
103            }
104        }
105
106        impl From<$name> for SEXP {
107            #[inline]
108            fn from(call: $name) -> SEXP {
109                call.0
110            }
111        }
112    };
113}
114
115impl 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);
117
118/// A caller's call is a call: hand it to an API that takes [`Call`].
119impl From<CallerCall> for Call {
120    #[inline]
121    fn from(call: CallerCall) -> Call {
122        Call(call.0)
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn markers_are_transparent_over_sexp() {
132        assert_eq!(std::mem::size_of::<Call>(), std::mem::size_of::<SEXP>());
133        assert_eq!(
134            std::mem::size_of::<CallerCall>(),
135            std::mem::size_of::<SEXP>()
136        );
137        let raw = SEXP::null();
138        let call = Call::from_sexp(raw);
139        assert_eq!(call.sexp(), raw);
140        assert_eq!(SEXP::from(call), raw);
141        let caller = CallerCall::from_sexp(raw);
142        assert_eq!(Call::from(caller), call);
143        assert_eq!(*caller, raw);
144    }
145}