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