Skip to main content

miniextendr_api/
altrep_bridge.rs

1//! Unsafe ALTREP trampolines and installers bridging safe traits to R's C ABI.
2//!
3//! This module is the seam between R's C method tables and the safe traits in
4//! [`crate::altrep_traits`]. It provides:
5//! - Generic `extern "C-unwind"` trampolines (`t_length`, `t_dataptr`,
6//!   `t_elt_*`, …) that call into safe trait methods on `T: Altrep`.
7//! - Installer functions (`install_*`) that register the trampolines with R
8//!   based on the `HAS_*` const gates declared by the safe traits.
9//!
10//! Non-allocating callbacks select `T::GUARD` at monomorphization time.
11//! Callbacks returning SEXP, Dataptr and string/list setters always use
12//! R_UnwindProtect: their generated bridges can allocate even when the
13//! underlying data provider is pure Rust.
14//!
15//! ## Why a separate bridge module
16//!
17//! R installs methods via raw `extern "C-unwind"` function pointers. To make
18//! `impl Altrep for MyType` ergonomic, we generate one trampoline per method
19//! once, generic over `T: Altrep`, that:
20//! 1. Recovers `T`'s monomorphization at the call site (no vtables).
21//! 2. Wraps the user callback in the per-type guard.
22//! 3. Hands the result back to R.
23//!
24//! Users (and the `#[derive(Altrep*)]` macros) never call into this module
25//! directly — `RegisterAltrep::get_or_init_class` does.
26//!
27//! ## Design
28//!
29//! Trampolines are only installed when `HAS_*` is true. When false, the method
30//! is NOT installed with R, so R uses its own default behavior.
31
32use crate::altrep_traits::{
33    AltComplex, AltInteger, AltList, AltLogical, AltRaw, AltReal, AltString, AltVec, Altrep,
34    AltrepGuard,
35};
36use crate::sys::altrep::R_altrep_class_t;
37use crate::{R_xlen_t, Rboolean, Rbyte, Rcomplex, SEXP, SEXPTYPE};
38use core::ffi::c_void;
39
40/// Dispatch an ALTREP callback through the guard mode selected by `T::GUARD`.
41///
42/// Since `T::GUARD` is a const, the compiler eliminates the unreachable branches
43/// at monomorphization time — zero runtime overhead for the chosen mode.
44///
45/// - `Unsafe`: No protection — the closure runs directly.
46/// - `RustUnwind`: Wraps in [`catch_unwind`](std::panic::catch_unwind), converting
47///   panics to `Rf_error` so they don't unwind through C frames.
48/// - `RUnwind`: Wraps in [`R_UnwindProtect`](crate::sys::R_UnwindProtect), catching
49///   both Rust panics and R longjmps safely.
50#[inline(always)]
51fn guarded_altrep_call<T: Altrep, F, R>(f: F) -> R
52where
53    F: FnOnce() -> R,
54{
55    match T::GUARD {
56        AltrepGuard::Unsafe => f(),
57        AltrepGuard::RustUnwind => crate::ffi_guard::guarded_ffi_call(
58            f,
59            crate::ffi_guard::GuardMode::CatchUnwind,
60            crate::panic_telemetry::PanicSource::Altrep,
61        ),
62        AltrepGuard::RUnwind => guarded_altrep_r_call(f),
63    }
64}
65
66/// Protect callbacks whose framework bridge may allocate in R, independently
67/// of the data provider's guard choice for native reads.
68#[inline(always)]
69fn guarded_altrep_r_call<R>(f: impl FnOnce() -> R) -> R {
70    crate::ffi_guard::guarded_ffi_call(
71        f,
72        crate::ffi_guard::GuardMode::RUnwind,
73        crate::panic_telemetry::PanicSource::Altrep,
74    )
75}
76
77/// Root an SEXP result before the guard allocates to signal deferred conditions.
78#[inline(always)]
79fn guarded_altrep_sexp_call(f: impl FnOnce() -> SEXP) -> SEXP {
80    let (result, _root) = guarded_altrep_r_call(|| {
81        let result = f();
82        // A C NULL result requests R's fallback in e.g. Duplicate/Extract_subset.
83        // SAFETY: every other result is a valid SEXP, on R's main thread.
84        let root = (!result.is_null_ptr()).then(|| unsafe { crate::OwnedProtect::new(result) });
85        (result, root)
86    });
87    result
88}
89
90// region: ALTREP BASE TRAMPOLINES
91
92/// Trampoline for Length method.
93/// # Safety
94/// `x` must be a valid SEXP for the ALTREP class backed by `T`.
95pub unsafe extern "C-unwind" fn t_length<T: Altrep>(x: SEXP) -> R_xlen_t {
96    guarded_altrep_call::<T, _, _>(|| T::length(x))
97}
98
99/// Trampoline for Duplicate method.
100/// # Safety
101/// `x` must be a valid SEXP for the ALTREP class backed by `T`.
102pub unsafe extern "C-unwind" fn t_duplicate<T: Altrep>(x: SEXP, deep: Rboolean) -> SEXP {
103    guarded_altrep_sexp_call(|| T::duplicate(x, #[allow(non_exhaustive_omitted_patterns)] match deep {
    Rboolean::TRUE => true,
    _ => false,
}matches!(deep, Rboolean::TRUE)))
104}
105
106/// Trampoline for DuplicateEX method (extended duplication).
107/// # Safety
108/// `x` must be a valid SEXP for the ALTREP class backed by `T`.
109pub unsafe extern "C-unwind" fn t_duplicate_ex<T: Altrep>(x: SEXP, deep: Rboolean) -> SEXP {
110    guarded_altrep_sexp_call(|| T::duplicate_ex(x, #[allow(non_exhaustive_omitted_patterns)] match deep {
    Rboolean::TRUE => true,
    _ => false,
}matches!(deep, Rboolean::TRUE)))
111}
112
113/// Trampoline for Inspect method.
114/// # Safety
115/// `x` must be a valid SEXP for the ALTREP class backed by `T`.
116pub unsafe extern "C-unwind" fn t_inspect<T: Altrep>(
117    x: SEXP,
118    pre: i32,
119    deep: i32,
120    pvec: i32,
121    inspect_subtree: Option<unsafe extern "C-unwind" fn(SEXP, i32, i32, i32)>,
122) -> Rboolean {
123    guarded_altrep_call::<T, _, _>(|| {
124        if T::inspect(x, pre, deep, pvec, inspect_subtree) {
125            Rboolean::TRUE
126        } else {
127            Rboolean::FALSE
128        }
129    })
130}
131
132/// Trampoline for Serialized_state method.
133/// # Safety
134/// `x` must be a valid SEXP for the ALTREP class backed by `T`.
135pub unsafe extern "C-unwind" fn t_serialized_state<T: Altrep>(x: SEXP) -> SEXP {
136    guarded_altrep_sexp_call(|| T::serialized_state(x))
137}
138
139/// Trampoline for Unserialize method.
140/// # Safety
141/// `class` and `state` must be valid SEXPs from R.
142pub unsafe extern "C-unwind" fn t_unserialize<T: Altrep>(class: SEXP, state: SEXP) -> SEXP {
143    guarded_altrep_sexp_call(|| T::unserialize(class, state))
144}
145
146/// Trampoline for UnserializeEX method (extended unserialization with attributes).
147/// # Safety
148/// `class`, `state`, and `attr` must be valid SEXPs from R.
149pub unsafe extern "C-unwind" fn t_unserialize_ex<T: Altrep>(
150    class: SEXP,
151    state: SEXP,
152    attr: SEXP,
153    objf: ::std::os::raw::c_int,
154    levs: ::std::os::raw::c_int,
155) -> SEXP {
156    guarded_altrep_sexp_call(|| T::unserialize_ex(class, state, attr, objf, levs))
157}
158
159/// Trampoline for Coerce method.
160/// # Safety
161/// `x` must be a valid SEXP for the ALTREP class backed by `T`.
162pub unsafe extern "C-unwind" fn t_coerce<T: Altrep>(x: SEXP, to_type: SEXPTYPE) -> SEXP {
163    guarded_altrep_sexp_call(|| T::coerce(x, to_type))
164}
165// endregion
166
167// region: ALTVEC TRAMPOLINES
168
169/// Trampoline for Dataptr method.
170/// # Safety
171/// `x` must be a valid SEXP for the ALTREP class backed by `T`.
172pub unsafe extern "C-unwind" fn t_dataptr<T: AltVec>(x: SEXP, w: Rboolean) -> *mut c_void {
173    guarded_altrep_r_call(|| T::dataptr(x, #[allow(non_exhaustive_omitted_patterns)] match w {
    Rboolean::TRUE => true,
    _ => false,
}matches!(w, Rboolean::TRUE)))
174}
175
176/// Trampoline for Dataptr_or_null method.
177/// # Safety
178/// `x` must be a valid SEXP for the ALTREP class backed by `T`.
179pub unsafe extern "C-unwind" fn t_dataptr_or_null<T: AltVec>(x: SEXP) -> *const c_void {
180    guarded_altrep_call::<T, _, _>(|| T::dataptr_or_null(x))
181}
182
183/// Trampoline for Extract_subset method.
184/// # Safety
185/// `x`, `indx`, and `call` must be valid SEXPs.
186pub unsafe extern "C-unwind" fn t_extract_subset<T: AltVec>(
187    x: SEXP,
188    indx: SEXP,
189    call: SEXP,
190) -> SEXP {
191    guarded_altrep_sexp_call(|| T::extract_subset(x, indx, call))
192}
193// endregion
194
195// region: ALTINTEGER TRAMPOLINES
196
197/// Trampoline for integer Elt method.
198/// # Safety
199/// `x` must be a valid ALTREP INTSXP and `i` within bounds.
200pub unsafe extern "C-unwind" fn t_int_elt<T: AltInteger>(x: SEXP, i: R_xlen_t) -> i32 {
201    guarded_altrep_call::<T, _, _>(|| T::elt(x, i))
202}
203
204/// Trampoline for integer Get_region method.
205/// # Safety
206/// `x` must be a valid ALTREP INTSXP and `out` a valid buffer of at least `n` elements.
207pub unsafe extern "C-unwind" fn t_int_get_region<T: AltInteger>(
208    x: SEXP,
209    i: R_xlen_t,
210    n: R_xlen_t,
211    out: *mut i32,
212) -> R_xlen_t {
213    if n <= 0 {
214        return 0;
215    }
216    let buf = unsafe { crate::altrep_impl::altrep_region_buf(out, n as usize) };
217    guarded_altrep_call::<T, _, _>(|| T::get_region(x, i, n, buf))
218}
219
220/// Trampoline for integer Is_sorted method.
221/// # Safety
222/// `x` must be a valid ALTREP INTSXP.
223pub unsafe extern "C-unwind" fn t_int_is_sorted<T: AltInteger>(x: SEXP) -> i32 {
224    guarded_altrep_call::<T, _, _>(|| T::is_sorted(x))
225}
226
227/// Trampoline for integer No_NA method.
228/// # Safety
229/// `x` must be a valid ALTREP INTSXP.
230pub unsafe extern "C-unwind" fn t_int_no_na<T: AltInteger>(x: SEXP) -> i32 {
231    guarded_altrep_call::<T, _, _>(|| T::no_na(x))
232}
233
234/// Trampoline for integer Sum method.
235/// # Safety
236/// `x` must be a valid ALTREP INTSXP.
237pub unsafe extern "C-unwind" fn t_int_sum<T: AltInteger>(x: SEXP, narm: Rboolean) -> SEXP {
238    guarded_altrep_sexp_call(|| T::sum(x, #[allow(non_exhaustive_omitted_patterns)] match narm {
    Rboolean::TRUE => true,
    _ => false,
}matches!(narm, Rboolean::TRUE)))
239}
240
241/// Trampoline for integer Min method.
242/// # Safety
243/// `x` must be a valid ALTREP INTSXP.
244pub unsafe extern "C-unwind" fn t_int_min<T: AltInteger>(x: SEXP, narm: Rboolean) -> SEXP {
245    guarded_altrep_sexp_call(|| T::min(x, #[allow(non_exhaustive_omitted_patterns)] match narm {
    Rboolean::TRUE => true,
    _ => false,
}matches!(narm, Rboolean::TRUE)))
246}
247
248/// Trampoline for integer Max method.
249/// # Safety
250/// `x` must be a valid ALTREP INTSXP.
251pub unsafe extern "C-unwind" fn t_int_max<T: AltInteger>(x: SEXP, narm: Rboolean) -> SEXP {
252    guarded_altrep_sexp_call(|| T::max(x, #[allow(non_exhaustive_omitted_patterns)] match narm {
    Rboolean::TRUE => true,
    _ => false,
}matches!(narm, Rboolean::TRUE)))
253}
254// endregion
255
256// region: ALTREAL TRAMPOLINES
257
258/// Trampoline for real Elt method.
259/// # Safety
260/// `x` must be a valid ALTREP REALSXP and `i` within bounds.
261pub unsafe extern "C-unwind" fn t_real_elt<T: AltReal>(x: SEXP, i: R_xlen_t) -> f64 {
262    guarded_altrep_call::<T, _, _>(|| T::elt(x, i))
263}
264
265/// Trampoline for real Get_region method.
266/// # Safety
267/// `x` must be a valid ALTREP REALSXP and `out` a valid buffer of at least `n` elements.
268pub unsafe extern "C-unwind" fn t_real_get_region<T: AltReal>(
269    x: SEXP,
270    i: R_xlen_t,
271    n: R_xlen_t,
272    out: *mut f64,
273) -> R_xlen_t {
274    if n <= 0 {
275        return 0;
276    }
277    let buf = unsafe { crate::altrep_impl::altrep_region_buf(out, n as usize) };
278    guarded_altrep_call::<T, _, _>(|| T::get_region(x, i, n, buf))
279}
280
281/// Trampoline for real Is_sorted method.
282/// # Safety
283/// `x` must be a valid ALTREP REALSXP.
284pub unsafe extern "C-unwind" fn t_real_is_sorted<T: AltReal>(x: SEXP) -> i32 {
285    guarded_altrep_call::<T, _, _>(|| T::is_sorted(x))
286}
287
288/// Trampoline for real No_NA method.
289/// # Safety
290/// `x` must be a valid ALTREP REALSXP.
291pub unsafe extern "C-unwind" fn t_real_no_na<T: AltReal>(x: SEXP) -> i32 {
292    guarded_altrep_call::<T, _, _>(|| T::no_na(x))
293}
294
295/// Trampoline for real Sum method.
296/// # Safety
297/// `x` must be a valid ALTREP REALSXP.
298pub unsafe extern "C-unwind" fn t_real_sum<T: AltReal>(x: SEXP, narm: Rboolean) -> SEXP {
299    guarded_altrep_sexp_call(|| T::sum(x, #[allow(non_exhaustive_omitted_patterns)] match narm {
    Rboolean::TRUE => true,
    _ => false,
}matches!(narm, Rboolean::TRUE)))
300}
301
302/// Trampoline for real Min method.
303/// # Safety
304/// `x` must be a valid ALTREP REALSXP.
305pub unsafe extern "C-unwind" fn t_real_min<T: AltReal>(x: SEXP, narm: Rboolean) -> SEXP {
306    guarded_altrep_sexp_call(|| T::min(x, #[allow(non_exhaustive_omitted_patterns)] match narm {
    Rboolean::TRUE => true,
    _ => false,
}matches!(narm, Rboolean::TRUE)))
307}
308
309/// Trampoline for real Max method.
310/// # Safety
311/// `x` must be a valid ALTREP REALSXP.
312pub unsafe extern "C-unwind" fn t_real_max<T: AltReal>(x: SEXP, narm: Rboolean) -> SEXP {
313    guarded_altrep_sexp_call(|| T::max(x, #[allow(non_exhaustive_omitted_patterns)] match narm {
    Rboolean::TRUE => true,
    _ => false,
}matches!(narm, Rboolean::TRUE)))
314}
315// endregion
316
317// region: ALTLOGICAL TRAMPOLINES
318
319/// Trampoline for logical Elt method.
320/// # Safety
321/// `x` must be a valid ALTREP LGLSXP and `i` within bounds.
322pub unsafe extern "C-unwind" fn t_lgl_elt<T: AltLogical>(x: SEXP, i: R_xlen_t) -> i32 {
323    guarded_altrep_call::<T, _, _>(|| T::elt(x, i))
324}
325
326/// Trampoline for logical Get_region method.
327/// # Safety
328/// `x` must be a valid ALTREP LGLSXP and `out` a valid buffer of at least `n` elements.
329pub unsafe extern "C-unwind" fn t_lgl_get_region<T: AltLogical>(
330    x: SEXP,
331    i: R_xlen_t,
332    n: R_xlen_t,
333    out: *mut i32,
334) -> R_xlen_t {
335    if n <= 0 {
336        return 0;
337    }
338    let buf = unsafe { crate::altrep_impl::altrep_region_buf(out, n as usize) };
339    guarded_altrep_call::<T, _, _>(|| T::get_region(x, i, n, buf))
340}
341
342/// Trampoline for logical Is_sorted method.
343/// # Safety
344/// `x` must be a valid ALTREP LGLSXP.
345pub unsafe extern "C-unwind" fn t_lgl_is_sorted<T: AltLogical>(x: SEXP) -> i32 {
346    guarded_altrep_call::<T, _, _>(|| T::is_sorted(x))
347}
348
349/// Trampoline for logical No_NA method.
350/// # Safety
351/// `x` must be a valid ALTREP LGLSXP.
352pub unsafe extern "C-unwind" fn t_lgl_no_na<T: AltLogical>(x: SEXP) -> i32 {
353    guarded_altrep_call::<T, _, _>(|| T::no_na(x))
354}
355
356/// Trampoline for logical Sum method.
357/// # Safety
358/// `x` must be a valid ALTREP LGLSXP.
359pub unsafe extern "C-unwind" fn t_lgl_sum<T: AltLogical>(x: SEXP, narm: Rboolean) -> SEXP {
360    guarded_altrep_sexp_call(|| T::sum(x, #[allow(non_exhaustive_omitted_patterns)] match narm {
    Rboolean::TRUE => true,
    _ => false,
}matches!(narm, Rboolean::TRUE)))
361}
362
363// Note: R's ALTREP API does not expose min/max for logical vectors
364// endregion
365
366// region: ALTRAW TRAMPOLINES
367
368/// Trampoline for raw Elt method.
369/// # Safety
370/// `x` must be a valid ALTREP RAWSXP and `i` within bounds.
371pub unsafe extern "C-unwind" fn t_raw_elt<T: AltRaw>(x: SEXP, i: R_xlen_t) -> Rbyte {
372    guarded_altrep_call::<T, _, _>(|| T::elt(x, i))
373}
374
375/// Trampoline for raw Get_region method.
376/// # Safety
377/// `x` must be a valid ALTREP RAWSXP and `out` a valid buffer of at least `n` elements.
378pub unsafe extern "C-unwind" fn t_raw_get_region<T: AltRaw>(
379    x: SEXP,
380    i: R_xlen_t,
381    n: R_xlen_t,
382    out: *mut Rbyte,
383) -> R_xlen_t {
384    if n <= 0 {
385        return 0;
386    }
387    let buf = unsafe { crate::altrep_impl::altrep_region_buf(out, n as usize) };
388    guarded_altrep_call::<T, _, _>(|| T::get_region(x, i, n, buf))
389}
390// endregion
391
392// region: ALTCOMPLEX TRAMPOLINES
393
394/// Trampoline for complex Elt method.
395/// # Safety
396/// `x` must be a valid ALTREP CPLXSXP and `i` within bounds.
397pub unsafe extern "C-unwind" fn t_cplx_elt<T: AltComplex>(x: SEXP, i: R_xlen_t) -> Rcomplex {
398    guarded_altrep_call::<T, _, _>(|| T::elt(x, i))
399}
400
401/// Trampoline for complex Get_region method.
402/// # Safety
403/// `x` must be a valid ALTREP CPLXSXP and `out` a valid buffer of at least `n` elements.
404pub unsafe extern "C-unwind" fn t_cplx_get_region<T: AltComplex>(
405    x: SEXP,
406    i: R_xlen_t,
407    n: R_xlen_t,
408    out: *mut Rcomplex,
409) -> R_xlen_t {
410    if n <= 0 {
411        return 0;
412    }
413    let buf = unsafe { crate::altrep_impl::altrep_region_buf(out, n as usize) };
414    guarded_altrep_call::<T, _, _>(|| T::get_region(x, i, n, buf))
415}
416// endregion
417
418// region: ALTSTRING TRAMPOLINES
419
420/// Trampoline for string Elt method (REQUIRED for ALTSTRING).
421/// # Safety
422/// `x` must be a valid ALTREP STRSXP and `i` within bounds.
423pub unsafe extern "C-unwind" fn t_str_elt<T: AltString>(x: SEXP, i: R_xlen_t) -> SEXP {
424    guarded_altrep_sexp_call(|| T::elt(x, i))
425}
426
427/// Trampoline for string Set_elt method.
428/// # Safety
429/// `x` must be a valid ALTREP STRSXP and `v` a valid CHARSXP.
430pub unsafe extern "C-unwind" fn t_str_set_elt<T: AltString>(x: SEXP, i: R_xlen_t, v: SEXP) {
431    guarded_altrep_r_call(|| T::set_elt(x, i, v))
432}
433
434/// Trampoline for string Is_sorted method.
435/// # Safety
436/// `x` must be a valid ALTREP STRSXP.
437pub unsafe extern "C-unwind" fn t_str_is_sorted<T: AltString>(x: SEXP) -> i32 {
438    guarded_altrep_call::<T, _, _>(|| T::is_sorted(x))
439}
440
441/// Trampoline for string No_NA method.
442/// # Safety
443/// `x` must be a valid ALTREP STRSXP.
444pub unsafe extern "C-unwind" fn t_str_no_na<T: AltString>(x: SEXP) -> i32 {
445    guarded_altrep_call::<T, _, _>(|| T::no_na(x))
446}
447// endregion
448
449// region: ALTLIST TRAMPOLINES
450
451/// Trampoline for list Elt method (REQUIRED for ALTLIST).
452/// # Safety
453/// `x` must be a valid ALTREP VECSXP and `i` within bounds.
454pub unsafe extern "C-unwind" fn t_list_elt<T: AltList>(x: SEXP, i: R_xlen_t) -> SEXP {
455    guarded_altrep_sexp_call(|| T::elt(x, i))
456}
457
458/// Trampoline for list Set_elt method.
459/// # Safety
460/// `x` must be a valid ALTREP VECSXP and `v` a valid SEXP.
461pub unsafe extern "C-unwind" fn t_list_set_elt<T: AltList>(x: SEXP, i: R_xlen_t, v: SEXP) {
462    guarded_altrep_r_call(|| T::set_elt(x, i, v))
463}
464// endregion
465
466// region: INSTALLERS - Only install methods where HAS_* = true
467
468/// Install base ALTREP methods (always installs length, conditionally installs optional).
469/// # Safety
470/// Must be called during R initialization with a valid ALTREP class handle.
471pub unsafe fn install_base<T: Altrep>(cls: R_altrep_class_t) {
472    // Length is ALWAYS installed (required)
473    unsafe { cls.set_length_method(Some(t_length::<T>)) };
474
475    // Optional methods - only install if HAS_* = true
476    if T::HAS_SERIALIZED_STATE {
477        unsafe { cls.set_serialized_state_method(Some(t_serialized_state::<T>)) };
478    }
479    if T::HAS_UNSERIALIZE {
480        unsafe { cls.set_unserialize_method(Some(t_unserialize::<T>)) };
481    }
482    if T::HAS_UNSERIALIZE_EX {
483        unsafe { cls.set_unserialize_ex_method(Some(t_unserialize_ex::<T>)) };
484    }
485    if T::HAS_DUPLICATE {
486        unsafe { cls.set_duplicate_method(Some(t_duplicate::<T>)) };
487    }
488    if T::HAS_DUPLICATE_EX {
489        unsafe { cls.set_duplicate_ex_method(Some(t_duplicate_ex::<T>)) };
490    }
491    if T::HAS_COERCE {
492        unsafe { cls.set_coerce_method(Some(t_coerce::<T>)) };
493    }
494    if T::HAS_INSPECT {
495        unsafe { cls.set_inspect_method(Some(t_inspect::<T>)) };
496    }
497}
498
499/// Install vector-level methods.
500/// # Safety
501/// Must be called during R initialization with a valid ALTREP class handle.
502pub unsafe fn install_vec<T: AltVec>(cls: R_altrep_class_t) {
503    if T::HAS_DATAPTR {
504        unsafe { cls.set_dataptr_method(Some(t_dataptr::<T>)) };
505    }
506    if T::HAS_DATAPTR_OR_NULL {
507        unsafe { cls.set_dataptr_or_null_method(Some(t_dataptr_or_null::<T>)) };
508    }
509    if T::HAS_EXTRACT_SUBSET {
510        unsafe { cls.set_extract_subset_method(Some(t_extract_subset::<T>)) };
511    }
512}
513
514/// Generate a family-specific installer function from a declarative spec.
515///
516/// Each entry maps a `HAS_*` const to a method on `R_altrep_class_t` and a trampoline.
517/// Optional `always` entries are installed unconditionally (e.g. required Elt).
518macro_rules! def_installer {
519    (
520        $(#[$meta:meta])*
521        $fn_name:ident < T: $trait:ident > {
522            $( $has:ident => $method:ident, $tramp:ident; )*
523        }
524    ) => {
525        $(#[$meta])*
526        pub unsafe fn $fn_name<T: $trait>(cls: R_altrep_class_t) {
527            $(
528                if T::$has { unsafe { cls.$method(Some($tramp::<T>)) } }
529            )*
530        }
531    };
532    (
533        $(#[$meta:meta])*
534        $fn_name:ident < T: $trait:ident > {
535            $( $has:ident => $method:ident, $tramp:ident; )*
536        }
537        always { $( $always_method:ident, $always_tramp:ident; )* }
538    ) => {
539        $(#[$meta])*
540        pub unsafe fn $fn_name<T: $trait>(cls: R_altrep_class_t) {
541            $(
542                unsafe { cls.$always_method(Some($always_tramp::<T>)) }
543            )*
544            $(
545                if T::$has { unsafe { cls.$method(Some($tramp::<T>)) } }
546            )*
547        }
548    };
549}
550
551#[doc = r" Install integer-specific methods."]
#[doc = r" # Safety"]
#[doc =
r" Must be called during R initialization with a valid ALTREP class handle."]
pub unsafe fn install_int<T: AltInteger>(cls: R_altrep_class_t) {
    if T::HAS_ELT {
        unsafe { cls.set_integer_elt_method(Some(t_int_elt::<T>)) }
    }
    if T::HAS_GET_REGION {
        unsafe {
            cls.set_integer_get_region_method(Some(t_int_get_region::<T>))
        }
    }
    if T::HAS_IS_SORTED {
        unsafe {
            cls.set_integer_is_sorted_method(Some(t_int_is_sorted::<T>))
        }
    }
    if T::HAS_NO_NA {
        unsafe { cls.set_integer_no_na_method(Some(t_int_no_na::<T>)) }
    }
    if T::HAS_SUM {
        unsafe { cls.set_integer_sum_method(Some(t_int_sum::<T>)) }
    }
    if T::HAS_MIN {
        unsafe { cls.set_integer_min_method(Some(t_int_min::<T>)) }
    }
    if T::HAS_MAX {
        unsafe { cls.set_integer_max_method(Some(t_int_max::<T>)) }
    }
}def_installer! {
552    /// Install integer-specific methods.
553    /// # Safety
554    /// Must be called during R initialization with a valid ALTREP class handle.
555    install_int<T: AltInteger> {
556        HAS_ELT => set_integer_elt_method, t_int_elt;
557        HAS_GET_REGION => set_integer_get_region_method, t_int_get_region;
558        HAS_IS_SORTED => set_integer_is_sorted_method, t_int_is_sorted;
559        HAS_NO_NA => set_integer_no_na_method, t_int_no_na;
560        HAS_SUM => set_integer_sum_method, t_int_sum;
561        HAS_MIN => set_integer_min_method, t_int_min;
562        HAS_MAX => set_integer_max_method, t_int_max;
563    }
564}
565
566#[doc = r" Install real-specific methods."]
#[doc = r" # Safety"]
#[doc =
r" Must be called during R initialization with a valid ALTREP class handle."]
pub unsafe fn install_real<T: AltReal>(cls: R_altrep_class_t) {
    if T::HAS_ELT {
        unsafe { cls.set_real_elt_method(Some(t_real_elt::<T>)) }
    }
    if T::HAS_GET_REGION {
        unsafe {
            cls.set_real_get_region_method(Some(t_real_get_region::<T>))
        }
    }
    if T::HAS_IS_SORTED {
        unsafe { cls.set_real_is_sorted_method(Some(t_real_is_sorted::<T>)) }
    }
    if T::HAS_NO_NA {
        unsafe { cls.set_real_no_na_method(Some(t_real_no_na::<T>)) }
    }
    if T::HAS_SUM {
        unsafe { cls.set_real_sum_method(Some(t_real_sum::<T>)) }
    }
    if T::HAS_MIN {
        unsafe { cls.set_real_min_method(Some(t_real_min::<T>)) }
    }
    if T::HAS_MAX {
        unsafe { cls.set_real_max_method(Some(t_real_max::<T>)) }
    }
}def_installer! {
567    /// Install real-specific methods.
568    /// # Safety
569    /// Must be called during R initialization with a valid ALTREP class handle.
570    install_real<T: AltReal> {
571        HAS_ELT => set_real_elt_method, t_real_elt;
572        HAS_GET_REGION => set_real_get_region_method, t_real_get_region;
573        HAS_IS_SORTED => set_real_is_sorted_method, t_real_is_sorted;
574        HAS_NO_NA => set_real_no_na_method, t_real_no_na;
575        HAS_SUM => set_real_sum_method, t_real_sum;
576        HAS_MIN => set_real_min_method, t_real_min;
577        HAS_MAX => set_real_max_method, t_real_max;
578    }
579}
580
581#[doc = r" Install logical-specific methods."]
#[doc = r" # Safety"]
#[doc =
r" Must be called during R initialization with a valid ALTREP class handle."]
pub unsafe fn install_lgl<T: AltLogical>(cls: R_altrep_class_t) {
    if T::HAS_ELT {
        unsafe { cls.set_logical_elt_method(Some(t_lgl_elt::<T>)) }
    }
    if T::HAS_GET_REGION {
        unsafe {
            cls.set_logical_get_region_method(Some(t_lgl_get_region::<T>))
        }
    }
    if T::HAS_IS_SORTED {
        unsafe {
            cls.set_logical_is_sorted_method(Some(t_lgl_is_sorted::<T>))
        }
    }
    if T::HAS_NO_NA {
        unsafe { cls.set_logical_no_na_method(Some(t_lgl_no_na::<T>)) }
    }
    if T::HAS_SUM {
        unsafe { cls.set_logical_sum_method(Some(t_lgl_sum::<T>)) }
    }
}def_installer! {
582    /// Install logical-specific methods.
583    /// # Safety
584    /// Must be called during R initialization with a valid ALTREP class handle.
585    install_lgl<T: AltLogical> {
586        HAS_ELT => set_logical_elt_method, t_lgl_elt;
587        HAS_GET_REGION => set_logical_get_region_method, t_lgl_get_region;
588        HAS_IS_SORTED => set_logical_is_sorted_method, t_lgl_is_sorted;
589        HAS_NO_NA => set_logical_no_na_method, t_lgl_no_na;
590        HAS_SUM => set_logical_sum_method, t_lgl_sum;
591    }
592}
593
594#[doc = r" Install raw-specific methods."]
#[doc = r" # Safety"]
#[doc =
r" Must be called during R initialization with a valid ALTREP class handle."]
pub unsafe fn install_raw<T: AltRaw>(cls: R_altrep_class_t) {
    if T::HAS_ELT { unsafe { cls.set_raw_elt_method(Some(t_raw_elt::<T>)) } }
    if T::HAS_GET_REGION {
        unsafe { cls.set_raw_get_region_method(Some(t_raw_get_region::<T>)) }
    }
}def_installer! {
595    /// Install raw-specific methods.
596    /// # Safety
597    /// Must be called during R initialization with a valid ALTREP class handle.
598    install_raw<T: AltRaw> {
599        HAS_ELT => set_raw_elt_method, t_raw_elt;
600        HAS_GET_REGION => set_raw_get_region_method, t_raw_get_region;
601    }
602}
603
604#[doc = r" Install complex-specific methods."]
#[doc = r" # Safety"]
#[doc =
r" Must be called during R initialization with a valid ALTREP class handle."]
pub unsafe fn install_cplx<T: AltComplex>(cls: R_altrep_class_t) {
    if T::HAS_ELT {
        unsafe { cls.set_complex_elt_method(Some(t_cplx_elt::<T>)) }
    }
    if T::HAS_GET_REGION {
        unsafe {
            cls.set_complex_get_region_method(Some(t_cplx_get_region::<T>))
        }
    }
}def_installer! {
605    /// Install complex-specific methods.
606    /// # Safety
607    /// Must be called during R initialization with a valid ALTREP class handle.
608    install_cplx<T: AltComplex> {
609        HAS_ELT => set_complex_elt_method, t_cplx_elt;
610        HAS_GET_REGION => set_complex_get_region_method, t_cplx_get_region;
611    }
612}
613
614#[doc = r" Install string-specific methods."]
#[doc = r" # Safety"]
#[doc =
r" Must be called during R initialization with a valid ALTREP class handle."]
#[doc = r" Note: Elt is always installed for ALTSTRING (required)."]
pub unsafe fn install_str<T: AltString>(cls: R_altrep_class_t) {
    unsafe { cls.set_string_elt_method(Some(t_str_elt::<T>)) }
    if T::HAS_SET_ELT {
        unsafe { cls.set_string_set_elt_method(Some(t_str_set_elt::<T>)) }
    }
    if T::HAS_IS_SORTED {
        unsafe { cls.set_string_is_sorted_method(Some(t_str_is_sorted::<T>)) }
    }
    if T::HAS_NO_NA {
        unsafe { cls.set_string_no_na_method(Some(t_str_no_na::<T>)) }
    }
}def_installer! {
615    /// Install string-specific methods.
616    /// # Safety
617    /// Must be called during R initialization with a valid ALTREP class handle.
618    /// Note: Elt is always installed for ALTSTRING (required).
619    install_str<T: AltString> {
620        HAS_SET_ELT => set_string_set_elt_method, t_str_set_elt;
621        HAS_IS_SORTED => set_string_is_sorted_method, t_str_is_sorted;
622        HAS_NO_NA => set_string_no_na_method, t_str_no_na;
623    }
624    always { set_string_elt_method, t_str_elt; }
625}
626
627#[doc = r" Install list-specific methods."]
#[doc = r" # Safety"]
#[doc =
r" Must be called during R initialization with a valid ALTREP class handle."]
#[doc = r" Note: Elt is always installed for ALTLIST (required)."]
pub unsafe fn install_list<T: AltList>(cls: R_altrep_class_t) {
    unsafe { cls.set_list_elt_method(Some(t_list_elt::<T>)) }
    if T::HAS_SET_ELT {
        unsafe { cls.set_list_set_elt_method(Some(t_list_set_elt::<T>)) }
    }
}def_installer! {
628    /// Install list-specific methods.
629    /// # Safety
630    /// Must be called during R initialization with a valid ALTREP class handle.
631    /// Note: Elt is always installed for ALTLIST (required).
632    install_list<T: AltList> {
633        HAS_SET_ELT => set_list_set_elt_method, t_list_set_elt;
634    }
635    always { set_list_elt_method, t_list_elt; }
636}
637// endregion