Skip to main content

miniextendr_api/externalptr/
sidecar.rs

1//! Typed sidecar fields: [`Sidecar<T>`] keeps a Rust-typed value in the
2//! external pointer's `prot` list.
3//!
4//! A Rust struct cannot root an R value: the GC doesn't trace Rust memory, so
5//! a `SEXP` stored in a struct field is freed as soon as R drops its own
6//! references. A `Sidecar<T>` field holds no data of its own once the struct
7//! is wrapped. Its value lives in the external pointer's protection list,
8//! after the type ID and the user slot, so the pointer roots it for as long as
9//! the pointer is reachable, and `saveRDS()` writes it with the pointer. The
10//! struct holds nothing that points back at its pointer: the value is read
11//! and written through the `ExternalPtr<T>` handle, which keeps the pointer
12//! alive for as long as it exists (#1856).
13//!
14//! The type is named in one place (this module) and re-exported from
15//! [`crate::externalptr`]; its final name is #1857.
16
17use std::any::{Any, TypeId};
18use std::cell::{Ref, RefCell, RefMut};
19use std::collections::HashMap;
20use std::fmt;
21use std::marker::PhantomData;
22use std::sync::Arc;
23
24use super::{
25    NO_HANDLE_CLASS, PROT_TYPE_ID_INDEX, PROT_VEC_LEN, TypedExternal, receiver_class,
26    refuse_restored, unwrap_class_handle,
27};
28use crate::condition::{ConditionCall, RCondition};
29use crate::from_r::TryFromSexp;
30use crate::into_r::IntoR;
31use crate::sys::{R_ExternalPtrAddr, R_ExternalPtrProtected};
32use crate::{R_xlen_t, SEXP, SEXPTYPE, SexpExt};
33
34// region: Sidecar<T>
35
36/// A sidecar field whose value R roots: it lives in the external pointer's
37/// protection list, typed as `T` on the Rust side.
38///
39/// Declare it as `#[r_data] pub keys: Sidecar<Vec<i32>>` on a
40/// `#[derive(ExternalPtr)]` struct. The derive generates accessors as
41/// associated functions of the struct, with the field's visibility. They take
42/// an owner handle, which keeps the pointer alive, not the struct:
43///
44/// | `#[r_data(...)]` | Rust accessors |
45/// |---|---|
46/// | `ref` | `fn keys(ptr: &impl SidecarOwner<Self>) -> T` |
47/// | `mut` | `fn set_keys(ptr: &mut impl SidecarOwnerMut<Self>, value: T)` |
48/// | `ref, mut`, or a bare `#[r_data]` | both |
49///
50/// Rust code sees no `SEXP`s, slot indices or other R plumbing: a plain
51/// `fn new() -> Self` constructor fills the field with [`Sidecar::new`], and
52/// a method that needs the value takes a view as its receiver
53/// (`self: ExternalPtrRef<'_, Self>` / `mut self: ExternalPtrMut<'_, Self>`)
54/// and calls `Self::keys(&self)` / `Self::set_keys(&mut self, v)`; other Rust
55/// code writes `Engine::keys(&ptr)`. The R side keeps its own accessors whatever the
56/// option: for a `pub` field, `Type_get_keys(x)` / `Type_set_keys(x, value)`,
57/// an R6 active binding (`r6(r_data_accessors)`), an S7 property
58/// (`s7(r_data_accessors)`), or `x$keys` / `x$keys <- value` on an S3, S4 or
59/// env class (`s3(r_data_accessors)` & co.). The R setter validates the new
60/// value with `TryFromSexp::<T>` and stores the value R gave.
61///
62/// ```ignore
63/// #[derive(ExternalPtr)]
64/// #[externalptr(r6)]
65/// pub struct Engine {
66///     n: i32,
67///     #[r_data] _r: RSidecar,
68///     #[r_data(ref, mut)] pub keys: Sidecar<Vec<i32>>,
69///     #[r_data(ref)] pub labels: Sidecar<Vec<String>>,
70///     #[r_data(ref, mut)] cache: Sidecar<Option<List>>,
71/// }
72///
73/// #[miniextendr(r6(r_data_accessors))]
74/// impl Engine {
75///     pub fn new(n: i32) -> Self {
76///         Engine {
77///             n,
78///             _r: RSidecar,
79///             keys: Sidecar::new((1..=n).collect()),
80///             labels: Sidecar::new(vec![]),
81///             cache: Sidecar::default(),
82///         }
83///     }
84///     pub fn key_count(self: ExternalPtrRef<'_, Self>) -> usize { Self::keys(&self).len() }
85///     pub fn remember(mut self: ExternalPtrMut<'_, Self>, value: List) {
86///         Self::set_cache(&mut self, Some(value))
87///     }
88/// }
89///
90/// // From any other Rust code holding the handle:
91/// let mut ptr = ExternalPtr::new(Engine::new(3));
92/// Engine::set_keys(&mut ptr, vec![9]);
93/// assert_eq!(Engine::keys(&ptr), vec![9]);
94/// ```
95///
96/// The accessors take [`super::ExternalPtrRef`] / [`super::ExternalPtrMut`]
97/// views (receivers, arguments, `borrow()` / `borrow_mut()`) or an
98/// `ExternalPtr`, which they borrow for the call: shared for the getter,
99/// exclusive for the setter. A plain `&self` receiver has no route back to
100/// an R owner.
101///
102/// # Values
103///
104/// - `T: IntoR + TryFromSexp`, with `T::Error: Display` for the getter's panic
105///   message.
106/// - A getter converts the slot with `TryFromSexp`, so a converting type
107///   (`Vec<i32>`, `String`) copies on every read. A conversion failure is a
108///   panic naming the field and the type; the framework turns it into an R
109///   error.
110/// - `Sidecar<SEXP>`, `Sidecar<List>` and `Sidecar<DataFrame>` keep an
111///   arbitrary R object unconverted. The getter returns a view that the slot
112///   roots only until the next write to that field; don't hold it across one.
113/// - [`Sidecar::new`] and [`Sidecar::replace`] convert their value with
114///   `IntoR` at once and root the R object it gives until a slot holds it
115///   (#2216). A fresh `SEXP` or list passed to them is safe from that moment;
116///   it needs its own protection only before the call.
117///
118/// # Lifecycle
119///
120/// - **Pending.** [`Sidecar::new(v)`](Sidecar::new) and [`Default`]
121///   (`Sidecar::new(T::default())`) convert `v` to its R object and hold it
122///   as a pending value in the struct, rooted in a main-thread pool until a
123///   slot holds it or the last copy drops. That is the only state a `Sidecar`
124///   has: before the struct is wrapped, after `*ptr = Engine::new(..)`, after
125///   `ExternalPtr::into_inner`. They run on R's main thread, or on the
126///   managed worker, which hands the conversion to the main thread as
127///   `ExternalPtr::new` does; any other thread panics. Like an owning
128///   `ExternalPtr`, the last copy of a pending value must be dropped on R's
129///   main thread or its managed worker.
130/// - **Flushed when wrapped.** `ExternalPtr::new` and the trait ABI's `mx_wrap`
131///   move every pending value into the pointer's protection list. From then
132///   on the value lives in R only, and the field is empty.
133/// - **Flushed on every access to the field.** The handle getter and setter,
134///   and the R getter and setter, first move that field's pending value into
135///   its slot (the setters drop it: the write supersedes it). So
136///   `*ptr.borrow_mut() = Engine::new(..)` or
137///   `ptr.borrow_mut().keys = Sidecar::new(v)` takes effect at the next
138///   access from either side. A
139///   `saveRDS()` before any access writes the old slot value.
140/// - **Loaded when the value leaves its pointer.** `ExternalPtr::into_inner`
141///   and `into_raw` read each slot of the pointer being consumed back into its
142///   field as a pending value (unless one is pending already), rooted as a
143///   new one is, so wrapping the value again keeps the values.
144/// - **A `self -> Self` method** leaves the values in the pointer across the
145///   call; the write-back flushes only what the method set with
146///   `Sidecar::new`. `Self { n: .., ..self }` keeps the values,
147///   `Self::new(..)` replaces them.
148/// - **Clone.** `Sidecar::clone` shares the pending R object and its root,
149///   which is none once the struct is wrapped. `ExternalPtr::clone` and
150///   `clone_from` copy the slot values as R objects, so the two pointers
151///   share them until one side writes the field: every write replaces the
152///   slot, and R copies a shared value before modifying it in place. A struct
153///   cloned outside its handle carries no sidecar values; clone the handle.
154/// - **`*ptr = new` and `mem::swap(&mut *a, &mut *b)`** are plain Rust
155///   moves. The sidecar values stay in each pointer's protection list; only
156///   pending values travel with the struct.
157/// - **After `readRDS()`** there is no Rust value. The standalone R
158///   accessors (`Type_get_f()` / `Type_set_f()`), the R6 active bindings and
159///   the S7 properties read and write the protection list directly, checked
160///   by the type ID stored with it. The `$` / `[[` / `$<-` / `[[<-` field
161///   methods of `s3(r_data_accessors)` & co. refuse the restored pointer
162///   instead, with the classed restored-object errors: they pass
163///   `require_live` to the same accessor (#1891).
164///
165/// Use the accessors on R's main thread only. Off the main thread an
166/// accessor panics with a clear message before it touches R.
167///
168/// # A save names the values by their R names
169///
170/// A saved object (`saveRDS()`, `serialize()`) stores one slot per `Sidecar`
171/// field, named after the field's R name: the `#[r_data(name = "..")]` value,
172/// else the Rust field name (#2187, #2215). After a reload, the standalone R
173/// accessors, the R6 bindings, the S7 properties and a rebuild hook
174/// (`#[externalptr(rebuild = path)]`, through [`super::Saved`]) find each
175/// field's slot by that name. So the R name is the save format:
176///
177/// - Reordering, adding or removing `Sidecar` fields keeps earlier saves
178///   readable. A field the save has no slot for raises the classed
179///   [`RESTORED_MISSING_SLOT_CLASS`](super::RESTORED_MISSING_SLOT_CLASS)
180///   error (or reads as `None` through a hook's `Self::try_<field>(saved)`).
181/// - Renaming the Rust field keeps them readable too, as long as the R name
182///   stays: a field without `name = ..` keeps its old ident as
183///   `#[r_data(name = "old")]`.
184/// - Renaming the R name orphans the field's slot in every earlier save: the
185///   field reads as one the save lacks.
186/// - Retyping a field keeps its name, so an earlier save's slot is read as the
187///   new type; a getter that can't convert it panics naming the field.
188///
189/// A save from another version of the crate (the `version` in its
190/// `Cargo.toml`, which the stored type ID records) is refused with the classed
191/// `miniextendr_restored_other_version` error (#1872), unless the type
192/// rebuilds it (`rebuild = path, any_version`).
193///
194/// # Methods that need sidecar values take a view
195///
196/// A `&self` / `&mut self` method sees the struct, which holds no reference
197/// to its pointer, so it can't reach the values. Take a view instead:
198/// `self: ExternalPtrRef<'_, Self>` to read, `mut self: ExternalPtrMut<'_, Self>`
199/// to write (`Deref` still gives the struct fields). The same holds for a
200/// struct moved out of its pointer through `&mut` (`mem::replace`,
201/// `mem::take`): it carries nothing to follow, so nothing can read a freed R
202/// object (#1856). A trait method takes its owner as
203/// `self: ExternalPtrRef<'_, Self>` or `self: ExternalPtrMut<'_, Self>`
204/// ([`ExternalPtrRef`](crate::ExternalPtrRef)), over an `ExternalPtr` or a
205/// trait-ABI object, and passes it to the same accessors. A `&self` trait
206/// method, such as one of a standard trait with a fixed signature, and the
207/// high-level ALTREP data traits receive only the struct (#1880).
208/// ALTREP derives also accept `#[r_data] Sidecar<T>` fields: with
209/// `#[altrep(manual, no_lowlevel)]`, low-level callbacks recover the handle
210/// through [`crate::altrep_data1_as_unchecked`] and call the associated
211/// accessors. Those derives emit no R class field wrappers.
212///
213/// A plain `SEXP` field under `#[r_data]` is a compile error: nothing would
214/// root it. Use `Sidecar<SEXP>`.
215pub struct Sidecar<T> {
216    /// The value the field holds while it is not in a pointer's slot.
217    pending: RefCell<Option<Pending>>,
218    /// The field's type: its accessors convert the slot to it, and the
219    /// struct's auto traits follow it as if it held a `T`.
220    _value: PhantomData<T>,
221}
222
223/// A value on its way into a slot: its R object, rooted from the moment the
224/// `Sidecar` holds it until a slot owns the GC edge (#2216).
225#[derive(#[automatically_derived]
impl ::core::clone::Clone for Pending {
    #[inline]
    fn clone(&self) -> Self {
        Pending {
            sexp: ::core::clone::Clone::clone(&self.sexp),
            root: ::core::clone::Clone::clone(&self.root),
            replace: ::core::clone::Clone::clone(&self.replace),
        }
    }
}Clone)]
226struct Pending {
227    sexp: SEXP,
228    root: Arc<PendingRoot>,
229    /// Made by [`Sidecar::replace`]: in a struct a rebuild hook returns, the
230    /// value replaces the slot the save kept (#2187).
231    replace: bool,
232}
233
234/// Only the pool key crosses threads, as with an owning `ExternalPtr`.
235/// Cloning a pending value needs no R call; the last copy releases the root.
236struct PendingRoot(crate::protect_pool::ProtectKey);
237
238impl Drop for PendingRoot {
239    fn drop(&mut self) {
240        let key = self.0;
241        crate::worker::with_r_thread(move || super::unroot_owned(key));
242    }
243}
244
245impl<T: IntoR + 'static> Sidecar<T> {
246    /// A sidecar field holding `value`, pending until the struct is wrapped
247    /// in an `ExternalPtr`.
248    ///
249    /// Converts `value` with `IntoR` and roots the R object until a slot
250    /// holds it, so a fresh R value is safe from here on.
251    ///
252    /// # Panics
253    ///
254    /// Off R's main thread and its managed worker, before converting; or
255    /// when the conversion fails.
256    pub fn new(value: T) -> Self {
257        Self::pending(value, false)
258    }
259
260    /// A sidecar field holding `value`, which replaces the slot a save kept
261    /// when a rebuild hook (`#[externalptr(rebuild = path)]`) returns it.
262    ///
263    /// A rebuilt object keeps each slot its save has: the hook's
264    /// [`Sidecar::new`] values are placeholders for those fields, and fill only
265    /// the fields the save lacks. A hook that recomputes a field's value, and
266    /// wants the object to hold the new value rather than the saved one,
267    /// returns it as `Sidecar::replace(value)`. Anywhere else it is
268    /// `Sidecar::new(value)`. Converts and roots `value` as
269    /// [`Sidecar::new`] does, with the same panics.
270    pub fn replace(value: T) -> Self {
271        Self::pending(value, true)
272    }
273
274    /// Converts `value` and roots its R object on R's main thread: inline
275    /// there, through the main thread from the managed worker (#2216).
276    fn pending(value: T, replace: bool) -> Self {
277        if !(crate::worker::is_r_main_thread() || crate::worker::has_worker_context())
    {
    {
        ::core::panicking::panic_fmt(format_args!("`Sidecar::{0}` converts its value on R\'s main thread: call it there or on the managed worker, not from a thread the package spawned",
                if replace { "replace" } else { "new" }));
    }
};assert!(
278            crate::worker::is_r_main_thread() || crate::worker::has_worker_context(),
279            "`Sidecar::{}` converts its value on R's main thread: call it there or on \
280             the managed worker, not from a thread the package spawned",
281            if replace { "replace" } else { "new" },
282        );
283        // `root_owned` roots the fresh object across the pool's growth.
284        let (sexp, key) = crate::worker::with_r_thread(move || {
285            let sexp = value.into_sexp();
286            (sexp, super::root_owned(sexp))
287        });
288        Self {
289            pending: RefCell::new(Some(Pending {
290                sexp,
291                root: Arc::new(PendingRoot(key)),
292                replace,
293            })),
294            _value: PhantomData,
295        }
296    }
297}
298
299impl<T> Sidecar<T> {
300    /// Whether the pending value was made by [`Sidecar::replace`].
301    fn replaces_saved(&self) -> bool {
302        self.pending
303            .borrow()
304            .as_ref()
305            .is_some_and(|pending| pending.replace)
306    }
307
308    /// Drops a pending value, which a write to the slot supersedes.
309    fn discard_pending(&self) {
310        *self.pending.borrow_mut() = None;
311    }
312
313    /// Moves a pending value into slot `index` of `prot`, if there is one.
314    /// On R's main thread; `set_vector_elt` allocates nothing.
315    fn flush_into(&self, prot: SEXP, index: usize) {
316        let Some(Pending { sexp, root, .. }) = self.pending.borrow_mut().take() else {
317            return;
318        };
319        prot.set_vector_elt(prot_index(index), sexp);
320        // The slot owns the GC edge now.
321        drop(root);
322    }
323
324    /// Reads slot `index` of `prot` back into the field as its pending value,
325    /// unless one is pending already. On R's main thread.
326    fn load_from(&self, prot: SEXP, index: usize) {
327        if self.pending.borrow().is_some() {
328            return;
329        }
330        // The old owner still roots the slot while the pool grows. Root the
331        // slot itself, not the old protection list, so independent pending
332        // fields do not retain one another's values.
333        let sexp = prot.vector_elt(prot_index(index));
334        *self.pending.borrow_mut() = Some(Pending {
335            sexp,
336            root: Arc::new(PendingRoot(super::root_owned(sexp))),
337            replace: false,
338        });
339    }
340}
341
342impl<T> Clone for Sidecar<T> {
343    /// A `Sidecar` sharing this field's pending R object, and its root, if
344    /// any. The value in a pointer's slot is not copied: clone the
345    /// `ExternalPtr`.
346    fn clone(&self) -> Self {
347        Self {
348            pending: RefCell::new(self.pending.borrow().clone()),
349            _value: PhantomData,
350        }
351    }
352}
353
354impl<T: Default + IntoR + 'static> Default for Sidecar<T> {
355    /// `Sidecar::new(T::default())`.
356    fn default() -> Self {
357        Self::new(T::default())
358    }
359}
360
361impl<T> fmt::Debug for Sidecar<T> {
362    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363        f.debug_struct("Sidecar")
364            .field("pending", &self.pending.borrow().is_some())
365            .finish()
366    }
367}
368
369/// The element of `prot` at `at`, the slot of `Sidecar` field `name`,
370/// converted to `T`.
371fn convert_slot<T: TryFromSexp>(prot: SEXP, at: R_xlen_t, name: &str, owner_type: &str) -> T
372where
373    <T as TryFromSexp>::Error: fmt::Display,
374{
375    let slot = prot.vector_elt(at);
376    match T::try_from_sexp(slot) {
377        Ok(value) => value,
378        Err(err) => {
    ::core::panicking::panic_fmt(format_args!("sidecar field `{1}` of `{2}` holds an R value that does not convert to `{0}`: {3}",
            std::any::type_name::<T>(), name, owner_type, err));
}panic!(
379            "sidecar field `{name}` of `{owner_type}` holds an R value that does not convert to `{}`: {err}",
380            std::any::type_name::<T>(),
381        ),
382    }
383}
384
385/// Panics unless this is R's main thread. No R API: the message names the
386/// field and the owning type.
387fn assert_main_thread(name: &str, owner_type: &str) {
388    if !crate::worker::is_r_main_thread() {
389        {
    ::core::panicking::panic_fmt(format_args!("sidecar field `{0}` of `{1}`: its accessors run on R\'s main thread only; this call came from another thread (a `worker` method, or a thread the package spawned)",
            name, owner_type));
};panic!(
390            "sidecar field `{name}` of `{owner_type}`: its accessors run on R's main thread only; \
391             this call came from another thread (a `worker` method, or a thread the \
392             package spawned)",
393        );
394    }
395}
396
397// endregion
398
399// region: The handle's view of the fields
400
401/// A `Sidecar<T>` field, as the handle sees it through the
402/// `TypedExternal::__mx_visit_sidecars` hook the derive emits. Every method
403/// runs on R's main thread.
404#[doc(hidden)]
405pub trait SidecarField {
406    /// Moves a pending value into slot `index` of `prot`.
407    fn __mx_flush(&self, prot: SEXP, index: usize);
408    /// Reads slot `index` of `prot` back into the field as its pending value,
409    /// unless one is pending already.
410    fn __mx_load(&self, prot: SEXP, index: usize);
411    /// Drops a pending value, which a write to the slot supersedes.
412    fn __mx_discard_pending(&self);
413    /// Whether the pending value was made by [`Sidecar::replace`].
414    fn __mx_replaces_saved(&self) -> bool;
415}
416
417impl<T> SidecarField for Sidecar<T> {
418    fn __mx_flush(&self, prot: SEXP, index: usize) {
419        self.flush_into(prot, index);
420    }
421
422    fn __mx_load(&self, prot: SEXP, index: usize) {
423        self.load_from(prot, index);
424    }
425
426    fn __mx_discard_pending(&self) {
427        self.discard_pending();
428    }
429
430    fn __mx_replaces_saved(&self) -> bool {
431        self.replaces_saved()
432    }
433}
434
435/// Moves every pending value of `value`'s `Sidecar` fields into its slot of
436/// `prot`, the protection list of the pointer holding `value`. On R's main
437/// thread.
438pub(crate) fn flush_sidecars<T: TypedExternal>(value: &T, prot: SEXP) {
439    value.__mx_visit_sidecars(&mut |index, _, field| field.__mx_flush(prot, index));
440}
441
442/// Reads every slot of `prot`, the protection list of the pointer `value` is
443/// leaving, back into its `Sidecar` field as a pending value (unless one is
444/// pending already). On R's main thread.
445pub(crate) fn load_sidecars<T: TypedExternal>(value: &T, prot: SEXP) {
446    value.__mx_visit_sidecars(&mut |index, _, field| field.__mx_load(prot, index));
447}
448
449/// Drops every pending value of `value`'s `Sidecar` fields.
450pub(crate) fn discard_pending_sidecars<T: TypedExternal>(value: &T) {
451    value.__mx_visit_sidecars(&mut |_, _, field| field.__mx_discard_pending());
452}
453
454/// Flushes a freshly wrapped value's pending values into its pointer's
455/// protection list: the three construction sites and
456/// `restore_after_consuming` call this once the `EXTPTRSXP` exists.
457///
458/// # Safety
459///
460/// On R's main thread; `any_raw` must own a `RefCell<T>`, and `owner` must be
461/// the live `EXTPTRSXP` holding it, with a `prot` list built by `ExternalPtr`.
462pub(super) unsafe fn init_sidecars<T: TypedExternal>(any_raw: *mut Box<dyn Any>, owner: SEXP) {
463    let value = unsafe { &*any_raw }
464        .downcast_ref::<RefCell<T>>()
465        .expect("ExternalPtr stores a T at construction")
466        .borrow();
467    let prot = unsafe { R_ExternalPtrProtected(owner) };
468    unsafe { name_slots::<T>(prot) };
469    flush_sidecars(&*value, prot);
470}
471
472/// Initializes the slots of a trait-ABI owner using the ordinary sidecar
473/// layout and visitation hook. The Rust allocation remains erased storage;
474/// it must never be wrapped as `ExternalPtr<T>` (which expects `Any` storage).
475///
476/// # Safety
477/// On R's main thread inside an R unwind boundary. `owner` must be the
478/// caller-rooted external pointer owning `value`, with its finalizer already
479/// registered.
480pub(crate) unsafe fn init_erased_sidecars<T: TypedExternal>(value: &T, owner: SEXP) {
481    unsafe {
482        let type_id = super::type_id_symbol_unchecked::<T>();
483        let prot =
484            crate::sys::Rf_allocVector_unchecked(SEXPTYPE::VECSXP, super::prot_vec_len::<T>());
485        // Attach before the next allocation (the slot names' attribute cell):
486        // the owner roots `prot` from here on.
487        crate::sys::R_SetExternalPtrProtected_unchecked(owner, prot);
488        prot.set_vector_elt_unchecked(PROT_TYPE_ID_INDEX, type_id);
489        name_slots::<T>(prot);
490        flush_sidecars(value, prot);
491    }
492}
493
494/// Copies every `Sidecar` slot of the pointer `source` into the pointer
495/// `target`: the two share the R values until one side writes a field.
496///
497/// # Safety
498///
499/// On R's main thread; both must be live `EXTPTRSXP`s built by `ExternalPtr`
500/// for a `T`. `set_vector_elt` allocates nothing.
501pub(super) unsafe fn copy_sidecar_slots<T: TypedExternal>(source: SEXP, target: SEXP) {
502    let from = unsafe { R_ExternalPtrProtected(source) };
503    let to = unsafe { R_ExternalPtrProtected(target) };
504    for index in 0..T::R_SLOT_COUNT {
505        let at = prot_index(index);
506        to.set_vector_elt(at, from.vector_elt(at));
507    }
508}
509
510/// Reads `T`'s `Sidecar` field `index` of the object `ptr` holds, after
511/// moving the field's pending value into the slot.
512///
513/// Called by the getters `#[derive(ExternalPtr)]` generates
514/// (`T::field(&ptr)`); `field` selects the struct's `Sidecar` field.
515///
516/// # Panics
517///
518/// Off R's main thread, naming the field, before touching R; or when the
519/// slot does not convert to `T`.
520#[doc(hidden)]
521pub fn sidecar_get<O, T>(
522    ptr: &impl super::SidecarOwner<O>,
523    index: usize,
524    name: &'static str,
525    field: fn(&O) -> &Sidecar<T>,
526) -> T
527where
528    O: TypedExternal,
529    T: IntoR + TryFromSexp,
530    <T as TryFromSexp>::Error: fmt::Display,
531{
532    assert_main_thread(name, O::TYPE_NAME);
533    // SAFETY (both arms): on R's main thread; the handle's EXTPTRSXP is live
534    // and was built for an `O`.
535    match ptr.__mx_read() {
536        super::SidecarRead::Live(value) => {
537            let prot = unsafe { R_ExternalPtrProtected(super::ExternalPtrRef::as_sexp(&value)) };
538            field(&value).flush_into(prot, index);
539            convert_slot::<T>(prot, prot_index(index), name, O::TYPE_NAME)
540        }
541        // A rebuild hook reads what the save kept, by the field's name;
542        // nothing is pending.
543        super::SidecarRead::Saved(sexp) => {
544            let prot = unsafe { R_ExternalPtrProtected(sexp) };
545            match saved_slot(prot, index, name) {
546                Some(at) => convert_slot::<T>(prot, at, name, O::TYPE_NAME),
547                None => missing_slot::<O>(name),
548            }
549        }
550    }
551}
552
553/// `T`'s `Sidecar` field `index`, named `name`, as the save `saved` kept it,
554/// or `None` when the save has no slot for it: a field added after the
555/// version that saved the object.
556///
557/// Called by the `try_<field>` getters `#[derive(ExternalPtr)]` generates for
558/// a type with a rebuild hook.
559///
560/// # Panics
561///
562/// Off R's main thread, naming the field; or when the slot does not convert
563/// to `T`.
564#[doc(hidden)]
565pub fn sidecar_try_saved<O, T>(
566    saved: &super::Saved<'_, O>,
567    index: usize,
568    name: &'static str,
569) -> Option<T>
570where
571    O: TypedExternal,
572    T: TryFromSexp,
573    <T as TryFromSexp>::Error: fmt::Display,
574{
575    assert_main_thread(name, O::TYPE_NAME);
576    // SAFETY: on R's main thread; `saved` holds a live EXTPTRSXP.
577    let prot = unsafe { R_ExternalPtrProtected(saved.as_sexp()) };
578    saved_slot(prot, index, name).map(|at| convert_slot::<T>(prot, at, name, O::TYPE_NAME))
579}
580
581/// Stores `value` in `T`'s `Sidecar` field `index` of the object `ptr` holds,
582/// dropping the field's pending value, which this write supersedes.
583///
584/// Called by the setters `#[derive(ExternalPtr)]` generates
585/// (`T::set_field(&mut ptr, value)`); `field` selects the struct's `Sidecar`
586/// field.
587///
588/// # Panics
589///
590/// Off R's main thread, naming the field, before touching R.
591#[doc(hidden)]
592pub fn sidecar_set<O, T>(
593    ptr: &mut impl super::SidecarOwnerMut<O>,
594    index: usize,
595    name: &'static str,
596    field: fn(&O) -> &Sidecar<T>,
597    value: T,
598) where
599    O: TypedExternal,
600    T: IntoR,
601{
602    assert_main_thread(name, O::TYPE_NAME);
603    let target = ptr.__mx_write();
604    // SAFETY: as in `sidecar_get`. Storing allocates nothing.
605    let prot = unsafe { R_ExternalPtrProtected(super::ExternalPtrMut::as_sexp(&target)) };
606    field(&target).discard_pending();
607    prot.set_vector_elt(prot_index(index), value.into_sexp());
608}
609
610// endregion
611
612// region: Slot names (#2187)
613
614#[doc =
r" Each type's slot names, preserved for the session and shared by every"]
#[doc =
r#" protection list of the type: `""` for the type ID and the user slot,"#]
#[doc = r" then the `Sidecar` fields' R names, in slot order."]
const SLOT_NAMES: ::std::thread::LocalKey<RefCell<HashMap<TypeId, SEXP>>> =
    {
        #[allow(mismatched_lifetime_syntaxes)]
        #[inline]
        fn __rust_std_internal_init_fn(_lifetime_elision:
                ::std::marker::PhantomData<&'static ()>)
            -> RefCell<HashMap<TypeId, SEXP>> {
            RefCell::new(HashMap::new())
        }
        unsafe {
            ::std::thread::LocalKey::new(const {
                        if ::std::mem::needs_drop::<RefCell<HashMap<TypeId,
                                    SEXP>>>() {
                            |__rust_std_internal_init|
                                {
                                    #[thread_local]
                                    static __RUST_STD_INTERNAL_VAL:
                                        ::std::thread::local_impl::LazyStorage<RefCell<HashMap<TypeId,
                                        SEXP>>, ()> =
                                        ::std::thread::local_impl::LazyStorage::new();
                                    __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init,
                                        || __rust_std_internal_init_fn(::std::marker::PhantomData))
                                }
                        } else {
                            |__rust_std_internal_init|
                                {
                                    #[thread_local]
                                    static __RUST_STD_INTERNAL_VAL:
                                        ::std::thread::local_impl::LazyStorage<RefCell<HashMap<TypeId,
                                        SEXP>>, !> =
                                        ::std::thread::local_impl::LazyStorage::new();
                                    __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init,
                                        || __rust_std_internal_init_fn(::std::marker::PhantomData))
                                }
                        }
                    })
        }
    };thread_local! {
615    /// Each type's slot names, preserved for the session and shared by every
616    /// protection list of the type: `""` for the type ID and the user slot,
617    /// then the `Sidecar` fields' R names, in slot order.
618    static SLOT_NAMES: RefCell<HashMap<TypeId, SEXP>> = RefCell::new(HashMap::new());
619}
620
621/// Names `prot`'s slots after `T`'s `Sidecar` fields. A save then keeps the
622/// names, so the version that reads it finds each field by name
623/// ([`saved_slot`]), whatever fields it added, removed or reordered since.
624/// The names vector is the type's own, built once; this allocates only the
625/// attribute's cell.
626///
627/// # Safety
628///
629/// On R's main thread, with `prot` a rooted protection list built for a `T`.
630pub(crate) unsafe fn name_slots<T: TypedExternal>(prot: SEXP) {
631    let cached = SLOT_NAMES.with_borrow(|names| names.get(&TypeId::of::<T>()).copied());
632    let names = cached.unwrap_or_else(|| {
633        // Preserved before it is filled, so the CHARSXPs it receives are
634        // reachable as they are made.
635        let names =
636            unsafe { crate::sys::Rf_allocVector(SEXPTYPE::STRSXP, super::prot_vec_len::<T>()) };
637        unsafe { crate::sys::R_PreserveObject(names) };
638        for (index, name) in T::SIDECAR_NAMES.iter().enumerate() {
639            names.set_string_elt(prot_index(index), SEXP::charsxp(name));
640        }
641        SLOT_NAMES.with_borrow_mut(|cache| cache.insert(TypeId::of::<T>(), names));
642        names
643    });
644    prot.set_names(names);
645}
646
647/// The position in the saved protection list `prot` of `Sidecar` field
648/// `index`, named `name`: found by name when the save carries slot names,
649/// else by position (a save by this version made before slots were named).
650/// `None` when the save has no slot for the field.
651pub(crate) fn saved_slot(prot: SEXP, index: usize, name: &str) -> Option<R_xlen_t> {
652    let names = prot.get_attr(SEXP::names_symbol());
653    if names.type_of() == SEXPTYPE::STRSXP && names.xlength() == prot.xlength() {
654        (PROT_VEC_LEN..names.xlength()).find(|&at| names.string_elt_str(at) == Some(name))
655    } else {
656        Some(prot_index(index)).filter(|&at| at < prot.xlength())
657    }
658}
659
660/// Whether the saved protection list `prot` carries slot names.
661pub(crate) fn has_slot_names(prot: SEXP) -> bool {
662    let names = prot.get_attr(SEXP::names_symbol());
663    names.type_of() == SEXPTYPE::STRSXP && names.xlength() == prot.xlength()
664}
665
666/// Raises the classed error for a read of `Sidecar` field `name` that a save
667/// of an `O` has no slot for.
668fn missing_slot<O: TypedExternal>(name: &str) -> ! {
669    RCondition::Error {
670        message: ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("the saved `{0}` has no `{1}` field: the version that saved it did not have it",
                O::TYPE_NAME, name))
    })format!(
671            "the saved `{}` has no `{name}` field: the version that saved it did not have it",
672            O::TYPE_NAME
673        ),
674        class: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [super::RESTORED_MISSING_SLOT_CLASS.to_owned()]))vec![super::RESTORED_MISSING_SLOT_CLASS.to_owned()],
675        data: Some(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [("field".to_owned(),
                    crate::RValue::Character(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                                [Some(name.to_owned())]))))]))vec![(
676            "field".to_owned(),
677            crate::RValue::Character(vec![Some(name.to_owned())]),
678        )]),
679        call: ConditionCall::Inherit,
680    }
681    .raise()
682}
683
684// endregion
685
686// region: The R accessors
687
688/// The protection-list position of `Sidecar` field `index`.
689pub(super) fn prot_index(index: usize) -> R_xlen_t {
690    let index = R_xlen_t::try_from(index).expect("sidecar index exceeds R_xlen_t::MAX");
691    PROT_VEC_LEN + index
692}
693
694/// Whether `sym` is the symbol of `T`'s type ID.
695///
696/// Compares names rather than installing `T`'s symbol, so it allocates nothing.
697fn is_type_id_symbol<T: TypedExternal>(sym: SEXP) -> bool {
698    if sym.type_of() != SEXPTYPE::SYMSXP {
699        return false;
700    }
701    let id = T::TYPE_ID_CSTR
702        .strip_suffix(b"\0")
703        .unwrap_or(T::TYPE_ID_CSTR);
704    let printname = sym.printname();
705    // SAFETY: a CHARSXP's data holds `len` bytes.
706    let name =
707        unsafe { std::slice::from_raw_parts(printname.r_char().cast::<u8>(), printname.len()) };
708    name == id
709}
710
711/// The external pointer an R accessor's receiver `x` carries: `x` itself, or
712/// the pointer an R6 / S4 / S7 handle, an environment or a list holds in
713/// `.ptr`, the shapes an instance method's receiver takes
714/// ([`unwrap_class_handle`]). So `Type_get_f(x)` and the `$` / `[[` field
715/// methods of `r_data_accessors` read a list that carries the handle (#1848).
716/// A bare pointer costs one `TYPEOF` compare; an S4 receiver reads its `ptr`
717/// slot through `methods::slot()`, which allocates. The pointer is reachable
718/// from `x`, which the caller's `.Call()` frame roots.
719///
720/// # Panics
721///
722/// When `x` carries no external pointer, with the classed
723/// [`NO_HANDLE_CLASS`] error ([`refuse_no_handle`]).
724///
725/// # Safety
726///
727/// Must be called from R's main thread with a valid `x`.
728unsafe fn sidecar_receiver<T: TypedExternal>(x: SEXP) -> SEXP {
729    if x.type_of() == SEXPTYPE::EXTPTRSXP {
730        return x;
731    }
732    match unsafe { unwrap_class_handle(x) } {
733        Some(ptr) => ptr,
734        None => refuse_no_handle::<T>(x),
735    }
736}
737
738/// What an R accessor's receiver holds instead of a live `T` (#1920).
739#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Refused { }
#[automatically_derived]
impl ::core::clone::Clone for Refused {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Refused { }Copy)]
740enum Refused {
741    /// No external pointer at all ([`refuse_no_handle`]).
742    NoPointer,
743    /// A pointer without an address that `T` did not build.
744    NullPointer,
745    /// A live pointer to another type.
746    OtherType,
747}
748
749/// The message of a receiver refusal (#1920): the type's own
750/// ([`TypedExternal::FIELD_ERROR_MESSAGE`]), else one naming `receiver`'s R
751/// class, which is what the user sees (the field methods dispatched on it),
752/// else, for a receiver without a class, one naming the Rust type.
753fn refusal_message<T: TypedExternal>(receiver: SEXP, refused: Refused) -> String {
754    if let Some(message) = T::FIELD_ERROR_MESSAGE {
755        return message.to_owned();
756    }
757    match (receiver_class(receiver), refused) {
758        (Some(class), Refused::NoPointer) => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this `{0}` object holds no pointer",
                class))
    })format!("this `{class}` object holds no pointer"),
759        (Some(class), Refused::NullPointer) => {
760            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this `{0}` object holds a null pointer",
                class))
    })format!("this `{class}` object holds a null pointer")
761        }
762        (Some(class), Refused::OtherType) => {
763            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this `{0}` object holds a pointer of another type",
                class))
    })format!("this `{class}` object holds a pointer of another type")
764        }
765        (None, Refused::NoPointer) => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected ExternalPtr<{0}>, got a non-external-pointer object",
                T::TYPE_NAME))
    })format!(
766            "expected ExternalPtr<{}>, got a non-external-pointer object",
767            T::TYPE_NAME
768        ),
769        (None, Refused::NullPointer) => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected ExternalPtr<{0}>, got a null external pointer",
                T::TYPE_NAME))
    })format!(
770            "expected ExternalPtr<{}>, got a null external pointer",
771            T::TYPE_NAME
772        ),
773        (None, Refused::OtherType) => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected ExternalPtr<{0}>",
                T::TYPE_NAME))
    })format!("expected ExternalPtr<{}>", T::TYPE_NAME),
774    }
775}
776
777/// Raises the error for a receiver that carries no external pointer (#1901):
778/// [`NO_HANDLE_CLASS`], with the type's field-error classes
779/// ([`TypedExternal::FIELD_ERROR_CLASS`], #1899) in front, and for a list or
780/// an environment, the shape an earlier release of a package may have
781/// written to disk, its restored classes
782/// ([`TypedExternal::RESTORED_ERROR_CLASS`]) in front of those: a package
783/// refuses such a save as it refuses a restored pointer, with its restored
784/// message ([`TypedExternal::RESTORED_ERROR_MESSAGE`]) when it sets one.
785/// Another receiver (an atomic vector given the class) was never saved, so
786/// it gets neither (#1919).
787#[cold]
788fn refuse_no_handle<T: TypedExternal>(receiver: SEXP) -> ! {
789    let saved_shape = #[allow(non_exhaustive_omitted_patterns)] match receiver.type_of() {
    SEXPTYPE::VECSXP | SEXPTYPE::ENVSXP => true,
    _ => false,
}matches!(receiver.type_of(), SEXPTYPE::VECSXP | SEXPTYPE::ENVSXP);
790    let restored = if saved_shape {
791        T::RESTORED_ERROR_CLASS
792    } else {
793        &[]
794    };
795    let message = match T::RESTORED_ERROR_MESSAGE {
796        Some(message) if saved_shape => message.to_owned(),
797        _ => refusal_message::<T>(receiver, Refused::NoPointer),
798    };
799    std::panic::panic_any(RCondition::Error {
800        message,
801        class: no_handle_classes(restored, T::FIELD_ERROR_CLASS),
802        data: None,
803        call: ConditionCall::Inherit,
804    })
805}
806
807/// The classes of [`refuse_no_handle`]: `restored`, then `field_error`, then
808/// [`NO_HANDLE_CLASS`], each class once at its last place. The two lists
809/// usually end in the package's one parent class (`["pkg_saved",
810/// "pkg_error"]` and `["pkg_field", "pkg_error"]`), which then follows both
811/// specific classes, as a class vector goes from specific to general.
812fn no_handle_classes(restored: &[&str], field_error: &[&str]) -> Vec<String> {
813    let all: Vec<&str> = restored
814        .iter()
815        .chain(field_error)
816        .chain(&[NO_HANDLE_CLASS])
817        .copied()
818        .collect();
819    all.iter()
820        .enumerate()
821        .filter(|&(i, c)| !all[i + 1..].contains(c))
822        .map(|(_, c)| (*c).to_owned())
823        .collect()
824}
825
826/// Raises the error for a receiver whose pointer is not a `T` (a pointer of
827/// another type, or one without an address that `T` did not build), worded
828/// by [`refusal_message`], with the type's field-error classes
829/// ([`TypedExternal::FIELD_ERROR_CLASS`], #1899) in front. A type without
830/// them gets the plain panic.
831#[cold]
832fn refuse_other_type<T: TypedExternal>(receiver: SEXP, refused: Refused) -> ! {
833    let message = refusal_message::<T>(receiver, refused);
834    if T::FIELD_ERROR_CLASS.is_empty() {
835        { ::core::panicking::panic_fmt(format_args!("{0}", message)); };panic!("{message}");
836    }
837    std::panic::panic_any(RCondition::Error {
838        message,
839        class: T::FIELD_ERROR_CLASS
840            .iter()
841            .map(|c| (*c).to_owned())
842            .collect(),
843        data: None,
844        call: ConditionCall::Inherit,
845    })
846}
847
848/// Reads the `require_live` flag the R accessors' `.Call()`s pass: `TRUE`
849/// from the `$` / `[[` / `$<-` / `[[<-` field methods, which refuse a
850/// restored pointer, `FALSE` from the standalone accessors, the R6 bindings
851/// and the S7 properties, which read a save (#1891). Anything but `TRUE`
852/// reads as `false`.
853#[doc(hidden)]
854pub fn sidecar_require_live(flag: SEXP) -> bool {
855    flag.as_logical() == Some(true)
856}
857
858/// The `prot` list of the external pointer `x` carries
859/// ([`sidecar_receiver`]), after checking that it points to a `T`, and the
860/// live `T`'s `RefCell` when it still has an address. Allocates nothing for a
861/// pointer, a list or an environment receiver.
862///
863/// A live pointer is checked by `Any::downcast`. A pointer whose address is
864/// NULL (restored from a saved session by `readRDS()` / `unserialize()`)
865/// still holds its sidecar values, so without `require_live` it is accepted
866/// when its stored type ID is `T`'s; one another version of the crate saved
867/// is refused with the classed
868/// [`RESTORED_OTHER_VERSION_CLASS`](super::RESTORED_OTHER_VERSION_CLASS)
869/// error. With `require_live` (the field methods of `r_data_accessors`)
870/// every restored pointer is refused through [`refuse_restored`], so a save
871/// by this version gets [`RESTORED_NO_VALUE_CLASS`](super::RESTORED_NO_VALUE_CLASS).
872///
873/// # Panics
874///
875/// When `x` carries no external pointer to a `T`, or, with `require_live`,
876/// no live one.
877///
878/// # Safety
879///
880/// Must be called from R's main thread with a valid `x`.
881unsafe fn checked_prot<'a, T: TypedExternal>(
882    receiver: SEXP,
883    require_live: bool,
884) -> (SEXP, Option<&'a RefCell<T>>) {
885    let x = unsafe { sidecar_receiver::<T>(receiver) };
886    let mut any_raw = unsafe { R_ExternalPtrAddr(x) }.cast::<Box<dyn Any>>();
887    // A reader that needs the value rebuilds a restored pointer first, when
888    // the type has a hook; a `Sidecar` read takes the saved slot as it is.
889    // Another version's save, under `any_version`, is rebuilt by any reader:
890    // the rebuild lays its slots out as this version's (#2187).
891    if any_raw.is_null()
892        && (require_live
893            || T::REBUILDS_ANY_VERSION
894                && #[allow(non_exhaustive_omitted_patterns)] match unsafe {
        super::classify_null_address::<T>(x)
    } {
    super::NullAddress::OtherVersion { .. } => true,
    _ => false,
}matches!(
895                    unsafe { super::classify_null_address::<T>(x) },
896                    super::NullAddress::OtherVersion { .. }
897                ))
898        && unsafe { super::rebuild_restored::<T>(x, receiver) }
899    {
900        any_raw = unsafe { R_ExternalPtrAddr(x) }.cast::<Box<dyn Any>>();
901    }
902    // Read after a rebuild, which gives the pointer a new list (#2187).
903    let prot = unsafe { R_ExternalPtrProtected(x) };
904    let live = if any_raw.is_null() {
905        if require_live
906            || !unsafe { super::stored_type_id_symbol(x) }.is_some_and(is_type_id_symbol::<T>)
907        {
908            // A save by this version (`require_live`) or by another version
909            // raises its own error here.
910            unsafe { refuse_restored::<T>(x, receiver) };
911            refuse_other_type::<T>(receiver, Refused::NullPointer);
912        }
913        None
914    } else {
915        if unsafe { super::stored_type_id_symbol(x) }.is_none() {
916            refuse_other_type::<T>(receiver, Refused::OtherType);
917        }
918        match unsafe { &*any_raw }.downcast_ref::<RefCell<T>>() {
919            Some(cell) => Some(cell),
920            None => refuse_other_type::<T>(receiver, Refused::OtherType),
921        }
922    };
923    // A save read by name may hold fewer slots than the type has fields:
924    // its reads look the field up ([`saved_slot`]).
925    if (live.is_some() || !has_slot_names(prot))
926        && !usize::try_from(prot_index(T::R_SLOT_COUNT)).is_ok_and(|len| len <= prot.len())
927    {
928        {
    ::core::panicking::panic_fmt(format_args!("ExternalPtr<{0}> has no sidecar slots: its protection list was not built by `ExternalPtr`",
            T::TYPE_NAME));
};panic!(
929            "ExternalPtr<{}> has no sidecar slots: its protection list was not built by `ExternalPtr`",
930            T::TYPE_NAME
931        );
932    }
933    (prot, live)
934}
935
936/// The live `T` of the external pointer `x` carries ([`sidecar_receiver`]),
937/// borrowed shared, for the R getters of a struct field (a field that is not
938/// a `Sidecar`). A pointer without an address is refused: with the classed
939/// restored-object errors when `T` built it ([`refuse_restored`]), else as a
940/// null pointer ([`refusal_message`]).
941///
942/// # Panics
943///
944/// When `x` carries no live external pointer to a `T`, or with the
945/// `miniextendr_borrow_conflict` R error while a running call holds the value
946/// mutably.
947///
948/// # Safety
949///
950/// Must be called from R's main thread with a valid `x`; the borrow must end
951/// while `x` keeps its value.
952#[doc(hidden)]
953pub unsafe fn sidecar_r_struct<'a, T: TypedExternal>(x: SEXP) -> Ref<'a, T> {
954    super::borrow_value(unsafe { sidecar_r_cell::<T>(x) })
955}
956
957/// The live `T` of the external pointer `x` carries, borrowed mutably, for
958/// the R setters of a struct field; see [`sidecar_r_struct`].
959///
960/// # Panics
961///
962/// When `x` carries no live external pointer to a `T`, or with the
963/// `miniextendr_borrow_conflict` R error while a running call holds the value.
964///
965/// # Safety
966///
967/// Must be called from R's main thread with a valid `x`; the borrow must end
968/// while `x` keeps its value.
969#[doc(hidden)]
970pub unsafe fn sidecar_r_struct_mut<'a, T: TypedExternal>(x: SEXP) -> RefMut<'a, T> {
971    super::borrow_value_mut(unsafe { sidecar_r_cell::<T>(x) })
972}
973
974/// The body of [`sidecar_r_struct`] and [`sidecar_r_struct_mut`]: the
975/// `RefCell` of the struct behind the pointer `x` carries
976/// ([`sidecar_receiver`]).
977unsafe fn sidecar_r_cell<'a, T: TypedExternal>(receiver: SEXP) -> &'a RefCell<T> {
978    let x = unsafe { sidecar_receiver::<T>(receiver) };
979    let mut any_raw = unsafe { R_ExternalPtrAddr(x) }.cast::<Box<dyn Any>>();
980    if any_raw.is_null() && unsafe { super::rebuild_restored::<T>(x, receiver) } {
981        any_raw = unsafe { R_ExternalPtrAddr(x) }.cast::<Box<dyn Any>>();
982    }
983    if any_raw.is_null() {
984        // A save by this or another version raises its own error here.
985        unsafe { refuse_restored::<T>(x, receiver) };
986        refuse_other_type::<T>(receiver, Refused::NullPointer);
987    }
988    if unsafe { super::stored_type_id_symbol(x) }.is_none() {
989        refuse_other_type::<T>(receiver, Refused::OtherType);
990    }
991    match unsafe { &*any_raw }.downcast_ref::<RefCell<T>>() {
992        Some(cell) => cell,
993        None => refuse_other_type::<T>(receiver, Refused::OtherType),
994    }
995}
996
997/// Checks that `x` carries an external pointer to a `T` ([`sidecar_receiver`]),
998/// live or (without `require_live`) read back by `readRDS()`, before a setter
999/// validates its value.
1000///
1001/// # Panics
1002///
1003/// When `x` carries no external pointer to a `T` ([`checked_prot`]), or with
1004/// the `miniextendr_borrow_conflict` R error while a running call holds the
1005/// value.
1006///
1007/// # Safety
1008///
1009/// Must be called from R's main thread with a valid `x`.
1010#[doc(hidden)]
1011pub unsafe fn sidecar_r_check<T: TypedExternal>(x: SEXP, require_live: bool) {
1012    let (_, live) = unsafe { checked_prot::<T>(x, require_live) };
1013    if let Some(cell) = live {
1014        drop(super::borrow_value_mut(cell));
1015    }
1016}
1017
1018/// Whether the receiver `x` (a pointer, or an object carrying one) still has
1019/// its Rust value, for the field methods of a type with
1020/// `#[externalptr(restored(refuse = "every_read"))]` (#1901), which ask
1021/// before a read that never reaches an accessor (`names()`, a name that is
1022/// not a field).
1023///
1024/// With `raise`, a receiver without one is refused as the accessors refuse
1025/// it ([`checked_prot`] with `require_live`: the type's restored error, the
1026/// no-pointer error, or the other-type error) and a live pointer of another
1027/// type is refused too, so the result is always `true`. Without it
1028/// (`.DollarNames()`, which serves completion and never raises) the answer
1029/// is whether `x` carries a pointer with an address: a list or an
1030/// environment without one, or an atomic vector, has none.
1031///
1032/// # Safety
1033///
1034/// Must be called from R's main thread with a valid `x`.
1035#[doc(hidden)]
1036pub unsafe fn sidecar_r_live<T: TypedExternal>(x: SEXP, raise: bool) -> bool {
1037    if raise {
1038        let _ = unsafe { checked_prot::<T>(x, true) };
1039        return true;
1040    }
1041    let ptr = if x.type_of() == SEXPTYPE::EXTPTRSXP {
1042        Some(x)
1043    } else {
1044        unsafe { unwrap_class_handle(x) }
1045    };
1046    ptr.is_some_and(|ptr| !unsafe { R_ExternalPtrAddr(ptr) }.is_null())
1047}
1048
1049/// Reads `T`'s `Sidecar` field `index` from the external pointer `x` carries
1050/// ([`sidecar_receiver`]).
1051///
1052/// Called by the R getters `#[derive(ExternalPtr)]` generates. A live
1053/// struct's pending value for the field is flushed into the slot first; a
1054/// pointer without an address (after `readRDS()`) reads its protection list
1055/// as is, unless `require_live` refuses it ([`checked_prot`]).
1056///
1057/// # Panics
1058///
1059/// When `x` carries no external pointer to a `T`, or with the
1060/// `miniextendr_borrow_conflict` R error while a running call holds the value
1061/// mutably.
1062///
1063/// # Safety
1064///
1065/// Must be called from R's main thread with a valid `x`.
1066#[doc(hidden)]
1067pub unsafe fn sidecar_r_get<T: TypedExternal>(x: SEXP, index: usize, require_live: bool) -> SEXP {
1068    let (prot, live) = unsafe { checked_prot::<T>(x, require_live) };
1069    let Some(cell) = live else {
1070        // A restored pointer: the slot of the field's name (#2187).
1071        let name = T::SIDECAR_NAMES[index];
1072        return match saved_slot(prot, index, name) {
1073            Some(at) => prot.vector_elt(at),
1074            None => missing_slot::<T>(name),
1075        };
1076    };
1077    super::borrow_value(cell).__mx_visit_sidecars(&mut |i, _, field| {
1078        if i == index {
1079            field.__mx_flush(prot, i);
1080        }
1081    });
1082    prot.vector_elt(prot_index(index))
1083}
1084
1085/// Stores `value`, which the caller validated with `TryFromSexp::<T>`, in
1086/// `T`'s `Sidecar` field `index` of the external pointer `x` carries
1087/// ([`sidecar_receiver`]).
1088///
1089/// Called by the R setters `#[derive(ExternalPtr)]` generates, after
1090/// [`sidecar_r_check`] with the same `require_live`. A live struct's pending
1091/// value for the field is dropped, since this write supersedes it. The store
1092/// allocates nothing; reading an S4 receiver's slot does, and `value`, an
1093/// argument of the setter's `.Call()`, is rooted by that call.
1094///
1095/// # Panics
1096///
1097/// When `x` carries no external pointer to a `T`, or with the
1098/// `miniextendr_borrow_conflict` R error while a running call holds the
1099/// value.
1100///
1101/// # Safety
1102///
1103/// Must be called from R's main thread with valid `x` and `value`.
1104#[doc(hidden)]
1105pub unsafe fn sidecar_r_set<T: TypedExternal>(
1106    x: SEXP,
1107    index: usize,
1108    value: SEXP,
1109    require_live: bool,
1110) {
1111    let (prot, live) = unsafe { checked_prot::<T>(x, require_live) };
1112    let Some(cell) = live else {
1113        // A restored pointer: the slot of the field's name (#2187).
1114        let name = T::SIDECAR_NAMES[index];
1115        match saved_slot(prot, index, name) {
1116            Some(at) => prot.set_vector_elt(at, value),
1117            None => missing_slot::<T>(name),
1118        }
1119        return;
1120    };
1121    super::borrow_value_mut(cell).__mx_visit_sidecars(&mut |i, _, field| {
1122        if i == index {
1123            field.__mx_discard_pending();
1124        }
1125    });
1126    prot.set_vector_elt(prot_index(index), value);
1127}
1128
1129// endregion
1130
1131#[cfg(test)]
1132mod tests {
1133    use super::no_handle_classes;
1134
1135    /// A parent class both lists share follows both specific classes.
1136    #[test]
1137    fn no_handle_classes_keep_each_class_at_its_last_place() {
1138        assert_eq!(
1139            no_handle_classes(&["pkg_saved", "pkg_error"], &["pkg_field", "pkg_error"]),
1140            [
1141                "pkg_saved",
1142                "pkg_field",
1143                "pkg_error",
1144                "miniextendr_no_handle"
1145            ]
1146        );
1147        assert_eq!(no_handle_classes(&[], &[]), ["miniextendr_no_handle"]);
1148        assert_eq!(
1149            no_handle_classes(&["pkg_error"], &[]),
1150            ["pkg_error", "miniextendr_no_handle"]
1151        );
1152    }
1153}