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