Skip to main content

miniextendr_api/altrep_impl/
macros.rs

1//! Declarative macros generating ALTREP trait implementations.
2//!
3//! These `#[macro_export]` macros are the public surface used by the
4//! `#[derive(Altrep*)]` proc-macros and by hand-rolled `optionals/*` impls.
5//! They expand to `impl Altrep`, `impl AltVec`, `impl Alt<Family>`, and the
6//! per-family `InferBase` impl for a given type.
7//!
8//! Each per-family `impl_alt<family>_from_data!` macro takes a type plus an
9//! optional knob list of `dataptr` / `serialize` / `subset` (in canonical
10//! alphabetical order). The default arm (no knobs) is the materialising
11//! path — `__impl_altvec_<family>_dataptr!` provides a trivial
12//! `AltrepDataptr<T>` returning `None`, falling through to data2
13//! materialisation.
14//!
15//! Each family also has an `impl_alt<family>_from_data_generic!` sibling
16//! accepting a brace-delimited generic parameter list and where-clause
17//! (`{$gen} $ty {$where}[, $knob]*`) for generic types like
18//! `struct Foo<T> { .. }`. The plain macros forward to their `_generic!`
19//! sibling with empty `{}` brackets, so each family has exactly one emission
20//! body regardless of which form is called.
21//!
22//! Note the iterator/stream data adaptors in `altrep_data::iter` /
23//! `altrep_data::stream` deliberately do **not** invoke these macros: they
24//! implement only the data-level traits and are meant to be wrapped by a
25//! concrete `#[derive(Altrep*)]` + `#[altrep(manual)]` struct (#1146).
26
27// region: Macros for generating trait implementations
28
29/// Generate ALTREP trait implementations for a type that implements AltIntegerData.
30///
31/// This macro generates `impl Altrep`, `impl AltVec`, and `impl AltInteger` for the type,
32/// delegating to the high-level `AltIntegerData` trait methods.
33///
34/// **Requires**: The type must implement `TypedExternal` (use `#[derive(ExternalPtr)]`).
35///
36/// ## Variants
37///
38/// ```ignore
39/// // Basic (no dataptr, no serialization):
40/// impl_altinteger_from_data!(MyType);
41///
42/// // With dataptr (type must implement AltrepDataptr<i32>):
43/// impl_altinteger_from_data!(MyType, dataptr);
44///
45/// // With serialization (type must implement AltrepSerialize):
46/// impl_altinteger_from_data!(MyType, serialize);
47///
48/// // With subset optimization (type must implement AltrepExtractSubset):
49/// impl_altinteger_from_data!(MyType, subset);
50///
51/// // Combine multiple options:
52/// impl_altinteger_from_data!(MyType, dataptr, serialize);
53/// impl_altinteger_from_data!(MyType, subset, serialize);
54/// ```
55#[macro_export]
56macro_rules! impl_altinteger_from_data {
57    // Default (no knobs): materializing DATAPTR — allocates INTSXP in data2 on
58    // first DATAPTR call. Without this, R's default DATAPTR errors with
59    // "cannot access data pointer". See `__impl_alt_family!` for the knob matrix.
60    ($ty:ty $(, $knob:ident)*) => {
61        $crate::impl_altinteger_from_data_generic!({} $ty {} $(, $knob)*);
62    };
63}
64
65/// Generic form of [`impl_altinteger_from_data!`]: accepts an optional
66/// generic parameter list and where-clause (`{T, U} Foo<T, U> {T: Bound, ..}`)
67/// so it can target `struct Foo<T> { .. }` types, not just
68/// concrete/monomorphic ones. The non-generic macro above forwards here with
69/// empty `{}` brackets so there is exactly one emission body for both call
70/// shapes.
71#[macro_export]
72macro_rules! impl_altinteger_from_data_generic {
73    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
74        $crate::__impl_alt_family!(
75            {$($gen)*} $ty {$($whr)*},
76            __impl_altinteger_methods,
77            impl_inferbase_integer,
78            dataptr: dataptr(i32),
79            default: materializing(i32)
80            $(, $knob)*
81        );
82    };
83}
84
85/// Internal macro: impl Altrep with just length, optionally plus serialization.
86///
87/// ## Arms
88///
89/// ```ignore
90/// __impl_altrep_base!($ty);                            // length only, default RUnwind guard
91/// __impl_altrep_base!($ty, $guard);                    // length only, explicit guard
92/// __impl_altrep_base!($ty, with_serialize);            // length + serialize, default guard
93/// __impl_altrep_base!($ty, $guard, with_serialize);    // length + serialize, explicit guard
94/// ```
95///
96/// The `with_serialize` flag implements both:
97/// - `serialized_state(x)` (save-side)
98/// - `unserialize(class, state)` (load-side)
99///
100/// The `unserialize` implementation reconstructs the backing Rust value via
101/// [`AltrepSerialize::unserialize`] and then creates a fresh ALTREP instance via
102/// `R_new_altrep(class, data1, SEXP::nil())` where `data1` is an `ExternalPtr<$ty>`.
103///
104/// This matches the proc-macro-generated `IntoR::into_sexp` behavior (data is stored in `data1`,
105/// and `data2` is `R_NilValue`).
106#[macro_export]
107#[doc(hidden)]
108macro_rules! __impl_altrep_base {
109    ($ty:ty) => {
110        $crate::__impl_altrep_base!($ty, RUnwind);
111    };
112    ($ty:ty, with_serialize) => {
113        $crate::__impl_altrep_base!($ty, RUnwind, with_serialize);
114    };
115    // Generic form: `{$gen} $ty {$where}, $guard[, with_serialize]`. Brace
116    // (not bracket/paren) delimiters are deliberate: `{` can never start a
117    // `$ty:ty` fragment, so a bare-`$ty:ty` arm asked to match a brace-led
118    // invocation fails cleanly instead of committing to (and hard-erroring
119    // on) a bogus type parse — `[]`/`()` are themselves valid (empty
120    // slice-like / unit) type starts and would NOT fail cleanly here. The
121    // non-generic arms above forward to this one with empty `{}` brackets so
122    // there is exactly one emission body per (guard, serialize) combination.
123    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $guard:ident $(, extra = {$($extra:item)*})?) => {
124        impl<$($gen)*> $crate::altrep_traits::Altrep for $ty where $($whr)* {
125            const GUARD: $crate::altrep_traits::AltrepGuard =
126                $crate::altrep_traits::AltrepGuard::$guard;
127
128            $($($extra)*)?
129
130            fn length(x: $crate::SEXP) -> $crate::R_xlen_t {
131                if let ::core::option::Option::Some(data2) = unsafe { $crate::altrep_data::materialized_altrep_data2(x) } {
132                    return $crate::SexpExt::len(&data2) as $crate::R_xlen_t;
133                }
134                let data =
135                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
136                <$ty as $crate::altrep_data::AltrepLen>::altrep_len(data) as $crate::R_xlen_t
137            }
138        }
139    };
140    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $guard:ident, with_serialize $(, extra = {$($extra:item)*})?) => {
141        impl<$($gen)*> $crate::altrep_traits::Altrep for $ty where $($whr)* {
142            const GUARD: $crate::altrep_traits::AltrepGuard =
143                $crate::altrep_traits::AltrepGuard::$guard;
144
145            $($($extra)*)?
146
147            fn length(x: $crate::SEXP) -> $crate::R_xlen_t {
148                if let ::core::option::Option::Some(data2) = unsafe { $crate::altrep_data::materialized_altrep_data2(x) } {
149                    return $crate::SexpExt::len(&data2) as $crate::R_xlen_t;
150                }
151                let data =
152                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
153                <$ty as $crate::altrep_data::AltrepLen>::altrep_len(data) as $crate::R_xlen_t
154            }
155
156            const HAS_SERIALIZED_STATE: bool = true;
157
158            fn serialized_state(x: $crate::SEXP) -> $crate::SEXP {
159                // data1 can be stale after writes to the R cache. C NULL asks R
160                // to serialize the current vector through its ordinary path.
161                if unsafe { $crate::altrep_data::altrep_values_written(x) } {
162                    return $crate::SEXP::null();
163                }
164                let data =
165                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
166                <$ty as $crate::altrep_data::AltrepSerialize>::serialized_state(data)
167            }
168
169            const HAS_UNSERIALIZE: bool = true;
170
171            fn unserialize(class: $crate::SEXP, state: $crate::SEXP) -> $crate::SEXP {
172                let ::core::option::Option::Some(data) = <$ty as $crate::altrep_data::AltrepSerialize>::unserialize(state)
173                else {
174                    ::core::panic!(
175                        "ALTREP unserialize failed for {}",
176                        ::core::any::type_name::<$ty>()
177                    );
178                };
179
180                // SAFETY: Unserialize is called by R on the main thread.
181                unsafe {
182                    use $crate::SEXP;
183                    use $crate::externalptr::ExternalPtr;
184                    use $crate::sys::altrep::R_altrep_class_t;
185                    use $crate::sys::{Rf_protect_unchecked, Rf_unprotect_unchecked};
186
187                    let ext_ptr = ExternalPtr::new_unchecked(data);
188                    let data1 = ext_ptr.as_sexp();
189                    // Protect across the allocation in new_altrep_unchecked.
190                    Rf_protect_unchecked(data1);
191                    let cls = R_altrep_class_t::from_sexp(class);
192                    let out = cls.new_altrep_unchecked(data1, SEXP::nil());
193                    Rf_unprotect_unchecked(1);
194                    out
195                }
196            }
197        }
198    };
199    // Bare-`$ty:ty` forwarders (concrete/monomorphic types) — listed AFTER
200    // the brace-generic arms above so a genuinely bare call (e.g. from the
201    // `#[derive(Altrep*)]` proc-macro, which always targets concrete types)
202    // still resolves here rather than being intercepted by the brace arms
203    // (which require a literal leading `{...}` group and so never match a
204    // bare invocation).
205    ($ty:ty, $guard:ident $(, extra = {$($extra:item)*})?) => {
206        $crate::__impl_altrep_base!({} $ty {}, $guard $(, extra = {$($extra)*})?);
207    };
208    ($ty:ty, $guard:ident, with_serialize $(, extra = {$($extra:item)*})?) => {
209        $crate::__impl_altrep_base!({} $ty {}, $guard, with_serialize $(, extra = {$($extra)*})?);
210    };
211}
212
213/// Internal macro: impl AltVec with dataptr support
214///
215/// When `writable = true`, obtains a mutable reference to the data via
216/// `altrep_data1_mut` so that writes through the returned pointer modify
217/// the Rust-owned data directly. When `writable = false`, uses the
218/// immutable `altrep_data1_as` + `dataptr_or_null` path, avoiding
219/// unnecessary mutable borrows (and, for `Cow`, avoiding a copy-on-write).
220#[macro_export]
221#[doc(hidden)]
222macro_rules! __impl_altvec_dataptr {
223    ($ty:ty, $elem:ty) => {
224        $crate::__impl_altvec_dataptr!({} $ty {}, $elem);
225    };
226    // Generic form: `[$gen] $ty [$where], $elem`. See `__impl_altrep_base!`
227    // for the empty-brackets delegation pattern.
228    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $elem:ty) => {
229        impl<$($gen)*> $crate::altrep_traits::AltVec for $ty where $($whr)* {
230            const HAS_DATAPTR: bool = true;
231
232            fn dataptr(x: $crate::SEXP, writable: bool) -> *mut ::core::ffi::c_void {
233                // Check data2 cache first (materialized by a prior call).
234                unsafe {
235                    if let ::core::option::Option::Some(data2) = $crate::altrep_data::materialized_altrep_data2(x) {
236                        return $crate::sys::DATAPTR_RO(data2).cast_mut();
237                    }
238                }
239
240                // Try the fast path: direct pointer from the underlying data.
241                let direct = if writable {
242                    let d = unsafe {
243                        <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_mut(x)
244                    };
245                    <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr(d, true)
246                        .map(|p| p.cast::<::core::ffi::c_void>())
247                } else {
248                    // Read-only: try immutable access first to avoid &mut borrows
249                    // and unnecessary copy-on-write for Cow types.
250                    // Scoped block: the &T borrow must end before altrep_extract_mut
251                    // to avoid aliasing &T / &mut T (Stacked Borrows UB).
252                    let ro = {
253                        let d = unsafe {
254                            <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
255                        };
256                        <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr_or_null(d)
257                    };
258                    if let ::core::option::Option::Some(p) = ro {
259                        return p.cast_mut().cast::<::core::ffi::c_void>();
260                    }
261                    // dataptr_or_null returned None — try mutable path.
262                    let d = unsafe {
263                        <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_mut(x)
264                    };
265                    <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr(d, false)
266                        .map(|p| p.cast::<::core::ffi::c_void>())
267                };
268
269                if let ::core::option::Option::Some(p) = direct {
270                    return p;
271                }
272
273                // The underlying data can't provide a contiguous pointer (e.g., Arrow
274                // array with null bitmask). Materialize into data2 via Elt methods.
275                // Must never return null — R doesn't fall back when custom Dataptr is set.
276                unsafe { $crate::altrep_data::materialize_altrep_data2::<$elem>(x) }
277            }
278
279            const HAS_DATAPTR_OR_NULL: bool = true;
280
281            fn dataptr_or_null(x: $crate::SEXP) -> *const ::core::ffi::c_void {
282                // Check data2 cache first (may have been materialized by a prior dataptr call)
283                unsafe {
284                    if let ::core::option::Option::Some(data2) = $crate::altrep_data::materialized_altrep_data2(x) {
285                        return $crate::sys::DATAPTR_RO(data2);
286                    }
287                }
288                let d =
289                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
290                <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr_or_null(d)
291                    .map(|p| p.cast::<::core::ffi::c_void>())
292                    .unwrap_or(::core::ptr::null())
293            }
294        }
295    };
296}
297
298/// Internal macro: impl AltVec with dataptr support for string ALTREP.
299///
300/// String vectors (STRSXP) store CHARSXP pointers, not contiguous data. This macro
301/// materializes remaining uncached elements into the data2 STRSXP cache (which may
302/// already have some elements from prior `Elt` calls). Returns the cached STRSXP's
303/// data pointer.
304#[macro_export]
305#[doc(hidden)]
306macro_rules! __impl_altvec_string_dataptr {
307    ($ty:ty) => {
308        $crate::__impl_altvec_string_dataptr!({} $ty {});
309    };
310    // Generic form: `[$gen] $ty [$where]`.
311    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
312        impl<$($gen)*> $crate::altrep_traits::AltVec for $ty where $($whr)* {
313            const HAS_DATAPTR: bool = true;
314
315            fn dataptr(x: $crate::SEXP, writable: bool) -> *mut ::core::ffi::c_void {
316                unsafe { $crate::altrep_impl::altstring_dataptr::<$ty>(x, writable) }
317            }
318
319            const HAS_DATAPTR_OR_NULL: bool = true;
320
321            fn dataptr_or_null(x: $crate::SEXP) -> *const ::core::ffi::c_void {
322                // Always return null. The data2 cache may still hold unfilled
323                // (C NULL) slots, which R must never read; null makes R go
324                // through Elt. Dataptr fills every slot before exposing it.
325                let _ = x;
326                ::core::ptr::null()
327            }
328        }
329    };
330}
331
332/// Internal macro: impl AltVec with a *materializing* dataptr for a given element type.
333///
334/// Thin wrapper, parameterised by element type: installs a trivial
335/// `AltrepDataptr<$elem>` (no direct pointer — `dataptr` returns `None`) and
336/// delegates to [`__impl_altvec_dataptr`], which materializes into `data2` via
337/// `RNativeType::elt`. The 5 per-family aliases below pin `$elem` so the derive
338/// and the public `impl_alt*_from_data!` macros can reference them by a stable
339/// per-family name.
340#[macro_export]
341#[doc(hidden)]
342macro_rules! __impl_altvec_materializing_dataptr {
343    ($ty:ty, $elem:ty) => {
344        $crate::__impl_altvec_materializing_dataptr!({} $ty {}, $elem);
345    };
346    // Generic form: `[$gen] $ty [$where], $elem`.
347    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $elem:ty) => {
348        impl<$($gen)*> $crate::altrep_data::AltrepDataptr<$elem> for $ty where $($whr)* {
349            fn dataptr(&mut self, _writable: bool) -> ::core::option::Option<*mut $elem> {
350                ::core::option::Option::None
351            }
352        }
353        $crate::__impl_altvec_dataptr!({$($gen)*} $ty {$($whr)*}, $elem);
354    };
355}
356
357/// Internal macro: materializing dataptr for logical ALTREP.
358///
359/// R logicals are stored as `i32` but accessed through `RLogical` for type safety.
360#[macro_export]
361#[doc(hidden)]
362macro_rules! __impl_altvec_logical_dataptr {
363    ($ty:ty) => {
364        $crate::__impl_altvec_materializing_dataptr!($ty, $crate::RLogical);
365    };
366}
367
368/// Internal macro: materializing dataptr for integer ALTREP.
369#[macro_export]
370#[doc(hidden)]
371macro_rules! __impl_altvec_integer_dataptr {
372    ($ty:ty) => {
373        $crate::__impl_altvec_materializing_dataptr!($ty, i32);
374    };
375}
376
377/// Internal macro: materializing dataptr for real ALTREP.
378#[macro_export]
379#[doc(hidden)]
380macro_rules! __impl_altvec_real_dataptr {
381    ($ty:ty) => {
382        $crate::__impl_altvec_materializing_dataptr!($ty, f64);
383    };
384}
385
386/// Internal macro: materializing dataptr for raw ALTREP.
387#[macro_export]
388#[doc(hidden)]
389macro_rules! __impl_altvec_raw_dataptr {
390    ($ty:ty) => {
391        $crate::__impl_altvec_materializing_dataptr!($ty, u8);
392    };
393}
394
395/// Internal macro: materializing dataptr for complex ALTREP.
396#[macro_export]
397#[doc(hidden)]
398macro_rules! __impl_altvec_complex_dataptr {
399    ($ty:ty) => {
400        $crate::__impl_altvec_materializing_dataptr!($ty, $crate::Rcomplex);
401    };
402}
403
404/// Internal macro: impl AltVec with extract_subset support
405#[macro_export]
406#[doc(hidden)]
407macro_rules! __impl_altvec_extract_subset {
408    ($ty:ty) => {
409        $crate::__impl_altvec_extract_subset!({} $ty {});
410    };
411    // Generic form: `[$gen] $ty [$where]`.
412    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
413        impl<$($gen)*> $crate::altrep_traits::AltVec for $ty where $($whr)* {
414            const HAS_EXTRACT_SUBSET: bool = true;
415
416            fn extract_subset(
417                x: $crate::SEXP,
418                indx: $crate::SEXP,
419                _call: $crate::SEXP,
420            ) -> $crate::SEXP {
421                // Validate that indx is an integer vector before calling INTEGER().
422                // Return C NULL (not R_NilValue) to signal R to use default
423                // subsetting if not — R checks `!= NULL` here.
424                if $crate::SexpExt::type_of(&indx) != $crate::SEXPTYPE::INTSXP {
425                    return $crate::SEXP::null();
426                }
427
428                // Once data2 exists R may have written to it (#1928), so the
429                // Rust data can be stale: let R subset the current vector.
430                if unsafe { $crate::altrep_data::altrep_values_written(x) } {
431                    return $crate::SEXP::null();
432                }
433
434                // Convert indx SEXP to slice using SexpExt (avoids raw-ptr-deref lint)
435                let indices = unsafe { $crate::SexpExt::as_slice::<i32>(&indx) };
436
437                let data =
438                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
439                <$ty as $crate::altrep_data::AltrepExtractSubset>::extract_subset(data, indices)
440                    .unwrap_or($crate::SEXP::null())
441            }
442        }
443    };
444}
445// endregion
446
447// region: Shared building-block macros for ALTREP trait implementations
448//
449// These macros expand to associated items inside `impl` blocks. They are
450// invoked by the per-family `__impl_alt*_methods!` macros to eliminate
451// code duplication across the 7 ALTREP type families.
452
453/// Shared `elt` implementation for ALTREP families with direct element access.
454///
455/// Generates `const HAS_ELT` and `fn elt(...)` inside an impl block.
456/// Used by integer, real, raw, and complex families.
457#[macro_export]
458#[doc(hidden)]
459macro_rules! __impl_alt_elt {
460    ($ty:ty, $trait:path, $elem:ty) => {
461        const HAS_ELT: bool = true;
462
463        fn elt(x: $crate::SEXP, i: $crate::R_xlen_t) -> $elem {
464            if let ::core::option::Option::Some(data2) =
465                unsafe { $crate::altrep_data::materialized_altrep_data2(x) }
466            {
467                return <$elem as $crate::RNativeType>::elt(data2, i.max(0));
468            }
469            let data =
470                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
471            <$ty as $trait>::elt(data, i.max(0) as usize)
472        }
473    };
474}
475
476/// Shared `get_region` implementation for ALTREP families.
477///
478/// Generates `const HAS_GET_REGION` and `fn get_region(...)` inside an impl block.
479/// Used by integer, real, logical, raw, and complex families.
480#[macro_export]
481#[doc(hidden)]
482macro_rules! __impl_alt_get_region {
483    ($ty:ty, $trait:path, $buf_ty:ty) => {
484        const HAS_GET_REGION: bool = true;
485
486        fn get_region(
487            x: $crate::SEXP,
488            start: $crate::R_xlen_t,
489            len: $crate::R_xlen_t,
490            buf: &mut [$buf_ty],
491        ) -> $crate::R_xlen_t {
492            if start < 0 || len <= 0 {
493                return 0;
494            }
495            if let ::core::option::Option::Some(data2) =
496                unsafe { $crate::altrep_data::materialized_altrep_data2(x) }
497            {
498                return unsafe {
499                    $crate::altrep_data::materialized_altrep_region::<$buf_ty>(
500                        data2,
501                        start as usize,
502                        len as usize,
503                        buf,
504                    )
505                } as $crate::R_xlen_t;
506            }
507            let data =
508                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
509            let len = len as usize;
510            <$ty as $trait>::get_region(data, start as usize, len, buf) as $crate::R_xlen_t
511        }
512    };
513}
514
515/// Shared `is_sorted` implementation for ALTREP families.
516///
517/// Generates `const HAS_IS_SORTED` and `fn is_sorted(...)` inside an impl block.
518/// Used by integer, real, logical, and string families.
519#[macro_export]
520#[doc(hidden)]
521macro_rules! __impl_alt_is_sorted {
522    ($ty:ty, $trait:path) => {
523        const HAS_IS_SORTED: bool = true;
524
525        fn is_sorted(x: $crate::SEXP) -> i32 {
526            if unsafe { $crate::altrep_data::altrep_values_written(x) } {
527                return $crate::altrep_traits::UNKNOWN_SORTEDNESS;
528            }
529            let data =
530                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
531            <$ty as $trait>::is_sorted(data)
532                .map(|s| s.to_r_int())
533                .unwrap_or(i32::MIN)
534        }
535    };
536}
537
538/// Shared `no_na` implementation for ALTREP families.
539///
540/// Generates `const HAS_NO_NA` and `fn no_na(...)` inside an impl block.
541/// Used by integer, real, logical, and string families.
542#[macro_export]
543#[doc(hidden)]
544macro_rules! __impl_alt_no_na {
545    ($ty:ty, $trait:path) => {
546        const HAS_NO_NA: bool = true;
547
548        fn no_na(x: $crate::SEXP) -> i32 {
549            if unsafe { $crate::altrep_data::altrep_values_written(x) } {
550                return 0;
551            }
552            let data =
553                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
554            <$ty as $trait>::no_na(data)
555                .map(|b| if b { 1 } else { 0 })
556                .unwrap_or(0)
557        }
558    };
559}
560// endregion
561
562// region: Canonical emission macros: __impl_altvec_flavor! / __impl_alt_from_data! / __impl_alt_family!
563//
564// Three layers, each with a single responsibility:
565//
566// 1. `__impl_altvec_flavor!` — maps an AltVec *flavor* token to the macro
567//    that emits the `impl AltVec` (typed direct pointer, materializing,
568//    STRSXP materialization, or extract_subset).
569// 2. `__impl_alt_from_data!` — the canonical emitter: Altrep base
570//    (with/without serialize) + AltVec flavor + family methods + InferBase.
571//    Exactly two arms — every knob combination reduces to these.
572// 3. `__impl_alt_family!` — the knob matrix: maps the public macros'
573//    user-facing knob spellings (`dataptr` / `serialize` / `subset`, in
574//    canonical order) to a flavor + optional serialize. Parameterised by the
575//    family's `dataptr:` and `default:` flavors so one matrix serves all
576//    six knob-bearing families.
577
578/// Internal macro: emit the `impl AltVec` for a given flavor.
579///
580/// Flavors:
581/// - `dataptr($elem)` — typed direct pointer via `AltrepDataptr<$elem>`,
582///   falling back to data2 materialization.
583/// - `materializing($elem)` — trivial `AltrepDataptr<$elem>` returning `None`
584///   plus the same `__impl_altvec_dataptr!` (pure data2 materialization).
585/// - `string_dataptr` — whole-vector STRSXP materialization (string family).
586/// - `subset` — `Extract_subset` support via `AltrepExtractSubset`.
587#[macro_export]
588#[doc(hidden)]
589macro_rules! __impl_altvec_flavor {
590    ($ty:ty, dataptr($elem:ty)) => {
591        $crate::__impl_altvec_dataptr!($ty, $elem);
592    };
593    ($ty:ty, materializing($elem:ty)) => {
594        $crate::__impl_altvec_materializing_dataptr!($ty, $elem);
595    };
596    ($ty:ty, string_dataptr) => {
597        $crate::__impl_altvec_string_dataptr!($ty);
598    };
599    ($ty:ty, subset) => {
600        $crate::__impl_altvec_extract_subset!($ty);
601    };
602    // Generic form: `[$gen] $ty [$where], $flavor`.
603    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, dataptr($elem:ty)) => {
604        $crate::__impl_altvec_dataptr!({$($gen)*} $ty {$($whr)*}, $elem);
605    };
606    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, materializing($elem:ty)) => {
607        $crate::__impl_altvec_materializing_dataptr!({$($gen)*} $ty {$($whr)*}, $elem);
608    };
609    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, string_dataptr) => {
610        $crate::__impl_altvec_string_dataptr!({$($gen)*} $ty {$($whr)*});
611    };
612    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, subset) => {
613        $crate::__impl_altvec_extract_subset!({$($gen)*} $ty {$($whr)*});
614    };
615}
616
617/// Internal macro: canonical ALTREP emission.
618///
619/// Generates the standard ALTREP trait implementations (Altrep base, AltVec
620/// flavor, family-specific methods, InferBase) for a given type. The two arms
621/// differ only in serialization support on the Altrep base impl.
622#[macro_export]
623#[doc(hidden)]
624macro_rules! __impl_alt_from_data {
625    ($ty:ty, $methods:ident, $inferbase:ident, $flavor:ident $(($elem:ty))?) => {
626        $crate::__impl_altrep_base!($ty);
627        $crate::__impl_altvec_flavor!($ty, $flavor $(($elem))?);
628        $crate::$methods!($ty);
629        $crate::$inferbase!($ty);
630    };
631    ($ty:ty, $methods:ident, $inferbase:ident, $flavor:ident $(($elem:ty))?, serialize) => {
632        $crate::__impl_altrep_base!($ty, with_serialize);
633        $crate::__impl_altvec_flavor!($ty, $flavor $(($elem))?);
634        $crate::$methods!($ty);
635        $crate::$inferbase!($ty);
636    };
637    // Generic form: `[$gen] $ty [$where], $methods, $inferbase, $flavor`.
638    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident, $flavor:ident $(($elem:ty))?) => {
639        $crate::__impl_altrep_base!({$($gen)*} $ty {$($whr)*}, RUnwind);
640        $crate::__impl_altvec_flavor!({$($gen)*} $ty {$($whr)*}, $flavor $(($elem))?);
641        $crate::$methods!({$($gen)*} $ty {$($whr)*});
642        $crate::$inferbase!({$($gen)*} $ty {$($whr)*});
643    };
644    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident, $flavor:ident $(($elem:ty))?, serialize) => {
645        $crate::__impl_altrep_base!({$($gen)*} $ty {$($whr)*}, RUnwind, with_serialize);
646        $crate::__impl_altvec_flavor!({$($gen)*} $ty {$($whr)*}, $flavor $(($elem))?);
647        $crate::$methods!({$($gen)*} $ty {$($whr)*});
648        $crate::$inferbase!({$($gen)*} $ty {$($whr)*});
649    };
650}
651
652/// Internal macro: the per-family knob matrix.
653///
654/// Maps the user-facing knob list of the public `impl_alt*_from_data!` macros
655/// (`dataptr` / `serialize` / `subset`, canonical order) to a canonical
656/// [`__impl_alt_from_data!`] invocation. The family supplies:
657/// - `dataptr:` — the AltVec flavor for the `dataptr` knob,
658/// - `default:` — the AltVec flavor when no `dataptr`/`subset` knob is given
659///   (the materializing/data2 path).
660///
661/// Valid knob combinations (anything else is a compile error):
662///
663/// ```ignore
664/// ()                    // default flavor
665/// (dataptr)             // direct-pointer flavor
666/// (serialize)           // default flavor + serialize
667/// (subset)              // extract_subset flavor
668/// (dataptr, serialize)
669/// (subset, serialize)
670/// ```
671#[macro_export]
672#[doc(hidden)]
673macro_rules! __impl_alt_family {
674    ($ty:ty, $methods:ident, $inferbase:ident,
675     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?) => {
676        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, $dff $(($dfe))?);
677    };
678    ($ty:ty, $methods:ident, $inferbase:ident,
679     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, dataptr) => {
680        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, $dpf $(($dpe))?);
681    };
682    ($ty:ty, $methods:ident, $inferbase:ident,
683     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, serialize) => {
684        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, $dff $(($dfe))?, serialize);
685    };
686    ($ty:ty, $methods:ident, $inferbase:ident,
687     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, subset) => {
688        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, subset);
689    };
690    ($ty:ty, $methods:ident, $inferbase:ident,
691     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, dataptr, serialize) => {
692        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, $dpf $(($dpe))?, serialize);
693    };
694    ($ty:ty, $methods:ident, $inferbase:ident,
695     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, subset, serialize) => {
696        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, subset, serialize);
697    };
698    // Generic form: `[$gen] $ty [$where], $methods, $inferbase, dataptr: .., default: ..[, knobs]`.
699    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
700     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?) => {
701        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, $dff $(($dfe))?);
702    };
703    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
704     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, dataptr) => {
705        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, $dpf $(($dpe))?);
706    };
707    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
708     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, serialize) => {
709        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, $dff $(($dfe))?, serialize);
710    };
711    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
712     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, subset) => {
713        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, subset);
714    };
715    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
716     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, dataptr, serialize) => {
717        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, $dpf $(($dpe))?, serialize);
718    };
719    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
720     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, subset, serialize) => {
721        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, subset, serialize);
722    };
723}
724// endregion
725
726// region: Per-family method macros (using shared building blocks)
727
728/// Internal macro for AltInteger method implementations.
729#[macro_export]
730#[doc(hidden)]
731macro_rules! __impl_altinteger_methods {
732    ($ty:ty) => {
733        $crate::__impl_altinteger_methods!({} $ty {});
734    };
735    // Generic form: `[$gen] $ty [$where]`.
736    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
737        impl<$($gen)*> $crate::altrep_traits::AltInteger for $ty where $($whr)* {
738            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltIntegerData, i32);
739            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltIntegerData, i32);
740            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltIntegerData);
741            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltIntegerData);
742
743            const HAS_SUM: bool = true;
744
745            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
746            fn sum(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
747                if unsafe { $crate::altrep_data::altrep_values_written(x) } {
748                    return $crate::SEXP::null();
749                }
750                let data =
751                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
752                match <$ty as $crate::altrep_data::AltIntegerData>::sum(data, narm) {
753                    ::core::option::Option::Some(s) => {
754                        if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
755                            $crate::SEXP::scalar_integer(s as i32)
756                        } else {
757                            $crate::SEXP::scalar_real(s as f64)
758                        }
759                    }
760                    ::core::option::Option::None => $crate::SEXP::null(),
761                }
762            }
763
764            const HAS_MIN: bool = true;
765
766            fn min(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
767                if unsafe { $crate::altrep_data::altrep_values_written(x) } {
768                    return $crate::SEXP::null();
769                }
770                let data =
771                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
772                <$ty as $crate::altrep_data::AltIntegerData>::min(data, narm)
773                    .map(|m| $crate::SEXP::scalar_integer(m))
774                    .unwrap_or($crate::SEXP::null())
775            }
776
777            const HAS_MAX: bool = true;
778
779            fn max(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
780                if unsafe { $crate::altrep_data::altrep_values_written(x) } {
781                    return $crate::SEXP::null();
782                }
783                let data =
784                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
785                <$ty as $crate::altrep_data::AltIntegerData>::max(data, narm)
786                    .map(|m| $crate::SEXP::scalar_integer(m))
787                    .unwrap_or($crate::SEXP::null())
788            }
789        }
790    };
791}
792
793/// Generate ALTREP trait implementations for a type that implements AltRealData.
794///
795/// ## Variants
796///
797/// ```ignore
798/// // Basic (no dataptr, no serialization):
799/// impl_altreal_from_data!(MyType);
800///
801/// // With dataptr (type must implement AltrepDataptr<f64>):
802/// impl_altreal_from_data!(MyType, dataptr);
803///
804/// // With serialization (type must implement AltrepSerialize):
805/// impl_altreal_from_data!(MyType, serialize);
806///
807/// // With subset optimization (type must implement AltrepExtractSubset):
808/// impl_altreal_from_data!(MyType, subset);
809///
810/// // Combine multiple options:
811/// impl_altreal_from_data!(MyType, dataptr, serialize);
812/// impl_altreal_from_data!(MyType, subset, serialize);
813/// ```
814#[macro_export]
815macro_rules! impl_altreal_from_data {
816    ($ty:ty $(, $knob:ident)*) => {
817        $crate::impl_altreal_from_data_generic!({} $ty {} $(, $knob)*);
818    };
819}
820
821/// Generic form of [`impl_altreal_from_data!`] — see
822/// [`impl_altinteger_from_data_generic!`] for the calling convention.
823#[macro_export]
824macro_rules! impl_altreal_from_data_generic {
825    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
826        $crate::__impl_alt_family!(
827            {$($gen)*} $ty {$($whr)*},
828            __impl_altreal_methods,
829            impl_inferbase_real,
830            dataptr: dataptr(f64),
831            default: materializing(f64)
832            $(, $knob)*
833        );
834    };
835}
836
837/// Internal macro for AltReal method implementations.
838#[macro_export]
839#[doc(hidden)]
840macro_rules! __impl_altreal_methods {
841    ($ty:ty) => {
842        $crate::__impl_altreal_methods!({} $ty {});
843    };
844    // Generic form: `[$gen] $ty [$where]`.
845    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
846        impl<$($gen)*> $crate::altrep_traits::AltReal for $ty where $($whr)* {
847            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltRealData, f64);
848            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltRealData, f64);
849            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltRealData);
850            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltRealData);
851
852            const HAS_SUM: bool = true;
853
854            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
855            fn sum(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
856                if unsafe { $crate::altrep_data::altrep_values_written(x) } {
857                    return $crate::SEXP::null();
858                }
859                let data =
860                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
861                <$ty as $crate::altrep_data::AltRealData>::sum(data, narm)
862                    .map(|s| $crate::SEXP::scalar_real(s))
863                    .unwrap_or($crate::SEXP::null())
864            }
865
866            const HAS_MIN: bool = true;
867
868            fn min(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
869                if unsafe { $crate::altrep_data::altrep_values_written(x) } {
870                    return $crate::SEXP::null();
871                }
872                let data =
873                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
874                <$ty as $crate::altrep_data::AltRealData>::min(data, narm)
875                    .map(|m| $crate::SEXP::scalar_real(m))
876                    .unwrap_or($crate::SEXP::null())
877            }
878
879            const HAS_MAX: bool = true;
880
881            fn max(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
882                if unsafe { $crate::altrep_data::altrep_values_written(x) } {
883                    return $crate::SEXP::null();
884                }
885                let data =
886                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
887                <$ty as $crate::altrep_data::AltRealData>::max(data, narm)
888                    .map(|m| $crate::SEXP::scalar_real(m))
889                    .unwrap_or($crate::SEXP::null())
890            }
891        }
892    };
893}
894
895/// Generate ALTREP trait implementations for a type that implements AltLogicalData.
896///
897/// ## Variants
898///
899/// ```ignore
900/// // Basic (no dataptr, no serialization):
901/// impl_altlogical_from_data!(MyType);
902///
903/// // With dataptr (type must implement AltrepDataptr<i32>):
904/// impl_altlogical_from_data!(MyType, dataptr);
905///
906/// // With serialization (type must implement AltrepSerialize):
907/// impl_altlogical_from_data!(MyType, serialize);
908///
909/// // With subset optimization (type must implement AltrepExtractSubset):
910/// impl_altlogical_from_data!(MyType, subset);
911///
912/// // Combine multiple options:
913/// impl_altlogical_from_data!(MyType, dataptr, serialize);
914/// impl_altlogical_from_data!(MyType, subset, serialize);
915/// ```
916#[macro_export]
917macro_rules! impl_altlogical_from_data {
918    // Note the asymmetry: the `dataptr` knob is typed `i32` (R's LGLSXP storage),
919    // while the materializing default goes through `RLogical` for type safety.
920    ($ty:ty $(, $knob:ident)*) => {
921        $crate::impl_altlogical_from_data_generic!({} $ty {} $(, $knob)*);
922    };
923}
924
925/// Generic form of [`impl_altlogical_from_data!`] — see
926/// [`impl_altinteger_from_data_generic!`] for the calling convention.
927#[macro_export]
928macro_rules! impl_altlogical_from_data_generic {
929    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
930        $crate::__impl_alt_family!(
931            {$($gen)*} $ty {$($whr)*},
932            __impl_altlogical_methods,
933            impl_inferbase_logical,
934            dataptr: dataptr(i32),
935            default: materializing($crate::RLogical)
936            $(, $knob)*
937        );
938    };
939}
940
941/// Internal macro: impl AltLogical methods from AltLogicalData
942#[macro_export]
943#[doc(hidden)]
944macro_rules! __impl_altlogical_methods {
945    ($ty:ty) => {
946        $crate::__impl_altlogical_methods!({} $ty {});
947    };
948    // Generic form: `[$gen] $ty [$where]`.
949    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
950        impl<$($gen)*> $crate::altrep_traits::AltLogical for $ty where $($whr)* {
951            // Logical elt is special: returns Logical → .to_r_int()
952            const HAS_ELT: bool = true;
953
954            fn elt(x: $crate::SEXP, i: $crate::R_xlen_t) -> i32 {
955                if let ::core::option::Option::Some(data2) = unsafe { $crate::altrep_data::materialized_altrep_data2(x) } {
956                    return <$crate::RLogical as $crate::RNativeType>::elt(data2, i.max(0)).to_i32();
957                }
958                let data =
959                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
960                let i = i.max(0) as usize;
961                if let ::core::option::Option::Some(values) = <$ty as $crate::altrep_data::AltLogicalData>::as_r_slice(data) {
962                    return values[i];
963                }
964                <$ty as $crate::altrep_data::AltLogicalData>::elt(data, i).to_r_int()
965            }
966
967            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltLogicalData, i32);
968            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltLogicalData);
969            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltLogicalData);
970
971            const HAS_SUM: bool = true;
972
973            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
974            fn sum(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
975                if unsafe { $crate::altrep_data::altrep_values_written(x) } {
976                    return $crate::SEXP::null();
977                }
978                let data =
979                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
980                match <$ty as $crate::altrep_data::AltLogicalData>::sum(data, narm) {
981                    ::core::option::Option::Some(s) => {
982                        if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
983                            $crate::SEXP::scalar_integer(s as i32)
984                        } else {
985                            $crate::SEXP::scalar_real(s as f64)
986                        }
987                    }
988                    ::core::option::Option::None => $crate::SEXP::null(),
989                }
990            }
991        }
992    };
993}
994
995/// Generate ALTREP trait implementations for a type that implements AltRawData.
996///
997/// ## Variants
998///
999/// ```ignore
1000/// // Basic (no dataptr, no serialization):
1001/// impl_altraw_from_data!(MyType);
1002///
1003/// // With dataptr (type must implement AltrepDataptr<u8>):
1004/// impl_altraw_from_data!(MyType, dataptr);
1005///
1006/// // With serialization (type must implement AltrepSerialize):
1007/// impl_altraw_from_data!(MyType, serialize);
1008///
1009/// // With subset optimization (type must implement AltrepExtractSubset):
1010/// impl_altraw_from_data!(MyType, subset);
1011///
1012/// // Combine multiple options:
1013/// impl_altraw_from_data!(MyType, dataptr, serialize);
1014/// impl_altraw_from_data!(MyType, subset, serialize);
1015/// ```
1016#[macro_export]
1017macro_rules! impl_altraw_from_data {
1018    ($ty:ty $(, $knob:ident)*) => {
1019        $crate::impl_altraw_from_data_generic!({} $ty {} $(, $knob)*);
1020    };
1021}
1022
1023/// Generic form of [`impl_altraw_from_data!`] — see
1024/// [`impl_altinteger_from_data_generic!`] for the calling convention.
1025#[macro_export]
1026macro_rules! impl_altraw_from_data_generic {
1027    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
1028        $crate::__impl_alt_family!(
1029            {$($gen)*} $ty {$($whr)*},
1030            __impl_altraw_methods,
1031            impl_inferbase_raw,
1032            dataptr: dataptr(u8),
1033            default: materializing(u8)
1034            $(, $knob)*
1035        );
1036    };
1037}
1038
1039/// Internal macro for AltRaw method implementations.
1040#[macro_export]
1041#[doc(hidden)]
1042macro_rules! __impl_altraw_methods {
1043    ($ty:ty) => {
1044        $crate::__impl_altraw_methods!({} $ty {});
1045    };
1046    // Generic form: `[$gen] $ty [$where]`.
1047    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
1048        impl<$($gen)*> $crate::altrep_traits::AltRaw for $ty where $($whr)* {
1049            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltRawData, u8);
1050            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltRawData, u8);
1051        }
1052    };
1053}
1054
1055/// Generate ALTREP trait implementations for a type that implements AltStringData.
1056///
1057/// ## Variants
1058///
1059/// ```ignore
1060/// // Basic (no serialization):
1061/// impl_altstring_from_data!(MyType);
1062///
1063/// // With dataptr (materialized STRSXP):
1064/// impl_altstring_from_data!(MyType, dataptr);
1065///
1066/// // With serialization (type must implement AltrepSerialize):
1067/// impl_altstring_from_data!(MyType, serialize);
1068///
1069/// // With subset optimization (type must implement AltrepExtractSubset):
1070/// impl_altstring_from_data!(MyType, subset);
1071///
1072/// // Combine multiple options:
1073/// impl_altstring_from_data!(MyType, dataptr, serialize);
1074/// impl_altstring_from_data!(MyType, subset, serialize);
1075/// ```
1076#[macro_export]
1077macro_rules! impl_altstring_from_data {
1078    // String vectors have no contiguous typed pointer; the default and `dataptr`
1079    // knobs both route through `string_dataptr` (whole-vector STRSXP materialization).
1080    ($ty:ty $(, $knob:ident)*) => {
1081        $crate::impl_altstring_from_data_generic!({} $ty {} $(, $knob)*);
1082    };
1083}
1084
1085/// Generic form of [`impl_altstring_from_data!`] — see
1086/// [`impl_altinteger_from_data_generic!`] for the calling convention.
1087#[macro_export]
1088macro_rules! impl_altstring_from_data_generic {
1089    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
1090        $crate::__impl_alt_family!(
1091            {$($gen)*} $ty {$($whr)*},
1092            __impl_altstring_methods,
1093            impl_inferbase_string,
1094            dataptr: string_dataptr,
1095            default: string_dataptr
1096            $(, $knob)*
1097        );
1098    };
1099}
1100
1101/// Internal macro for AltString method implementations.
1102#[macro_export]
1103#[doc(hidden)]
1104macro_rules! __impl_altstring_methods {
1105    ($ty:ty) => {
1106        $crate::__impl_altstring_methods!({} $ty {});
1107    };
1108    // Generic form: `[$gen] $ty [$where]`.
1109    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
1110        impl<$($gen)*> $crate::altrep_traits::AltString for $ty where $($whr)* {
1111            // Each element is computed once into the data2 cache; see
1112            // `altrep_impl::altstring_elt` for the cache convention.
1113            fn elt(x: $crate::SEXP, i: $crate::R_xlen_t) -> $crate::SEXP {
1114                unsafe { $crate::altrep_impl::altstring_elt::<$ty>(x, i) }
1115            }
1116
1117            const HAS_SET_ELT: bool = true;
1118
1119            fn set_elt(x: $crate::SEXP, i: $crate::R_xlen_t, v: $crate::SEXP) {
1120                unsafe { $crate::altrep_impl::altstring_set_elt::<$ty>(x, i, v) }
1121            }
1122
1123            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltStringData);
1124            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltStringData);
1125        }
1126    };
1127}
1128
1129/// Generate ALTREP trait implementations for a type that implements AltListData.
1130#[macro_export]
1131macro_rules! impl_altlist_from_data {
1132    ($ty:ty) => {
1133        $crate::impl_altlist_from_data!($ty, RUnwind);
1134    };
1135    ($ty:ty, $guard:ident) => {
1136        $crate::impl_altlist_from_data_generic!({} $ty {}, $guard);
1137    };
1138}
1139
1140/// Generic form of [`impl_altlist_from_data!`]: accepts an optional generic
1141/// parameter list and where-clause so it can target `struct Foo<T> { .. }`
1142/// types, not just concrete ones. The non-generic macro above forwards
1143/// here with empty `{}` brackets so there is exactly one emission body.
1144#[macro_export]
1145macro_rules! impl_altlist_from_data_generic {
1146    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
1147        $crate::impl_altlist_from_data_generic!({$($gen)*} $ty {$($whr)*}, RUnwind);
1148    };
1149    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $guard:ident) => {
1150        impl<$($gen)*> $crate::altrep_traits::Altrep for $ty where $($whr)* {
1151            const GUARD: $crate::altrep_traits::AltrepGuard =
1152                $crate::altrep_traits::AltrepGuard::$guard;
1153
1154            fn length(x: $crate::SEXP) -> $crate::R_xlen_t {
1155                let data =
1156                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1157                <$ty as $crate::altrep_data::AltrepLen>::altrep_len(data) as $crate::R_xlen_t
1158            }
1159        }
1160
1161        impl<$($gen)*> $crate::altrep_traits::AltVec for $ty where $($whr)* {}
1162
1163        impl<$($gen)*> $crate::altrep_traits::AltList for $ty where $($whr)* {
1164            fn elt(x: $crate::SEXP, i: $crate::R_xlen_t) -> $crate::SEXP {
1165                if let ::core::option::Option::Some(data2) = unsafe { $crate::altrep_data::materialized_altrep_data2(x) } {
1166                    return $crate::SexpExt::vector_elt(&data2, i);
1167                }
1168                let data =
1169                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1170                <$ty as $crate::altrep_data::AltListData>::elt(data, i.max(0) as usize)
1171            }
1172
1173            const HAS_SET_ELT: bool = true;
1174
1175            fn set_elt(x: $crate::SEXP, i: $crate::R_xlen_t, v: $crate::SEXP) {
1176                unsafe { $crate::altrep_impl::altlist_set_elt::<$ty>(x, i, v) }
1177            }
1178        }
1179
1180        $crate::impl_inferbase_list!({$($gen)*} $ty {$($whr)*});
1181    };
1182}
1183
1184/// Internal macro: impl AltComplex methods (elt, get_region)
1185#[macro_export]
1186#[doc(hidden)]
1187macro_rules! __impl_altcomplex_methods {
1188    ($ty:ty) => {
1189        $crate::__impl_altcomplex_methods!({} $ty {});
1190    };
1191    // Generic form: `[$gen] $ty [$where]`.
1192    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
1193        impl<$($gen)*> $crate::altrep_traits::AltComplex for $ty where $($whr)* {
1194            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltComplexData, $crate::Rcomplex);
1195            $crate::__impl_alt_get_region!(
1196                $ty,
1197                $crate::altrep_data::AltComplexData,
1198                $crate::Rcomplex
1199            );
1200        }
1201    };
1202}
1203
1204/// Generate ALTREP trait implementations for a type that implements AltComplexData.
1205///
1206/// Optional features can be enabled by passing additional arguments:
1207/// - `dataptr`: Enable `Dataptr` and `Dataptr_or_null` methods (requires `AltrepDataptr<Rcomplex>`)
1208/// - `serialize`: Enable serialization support (requires `AltrepSerialize`)
1209/// - `subset`: Enable optimized subsetting (requires `AltrepExtractSubset`)
1210#[macro_export]
1211macro_rules! impl_altcomplex_from_data {
1212    ($ty:ty $(, $knob:ident)*) => {
1213        $crate::impl_altcomplex_from_data_generic!({} $ty {} $(, $knob)*);
1214    };
1215}
1216
1217/// Generic form of [`impl_altcomplex_from_data!`] — see
1218/// [`impl_altinteger_from_data_generic!`] for the calling convention.
1219#[macro_export]
1220macro_rules! impl_altcomplex_from_data_generic {
1221    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
1222        $crate::__impl_alt_family!(
1223            {$($gen)*} $ty {$($whr)*},
1224            __impl_altcomplex_methods,
1225            impl_inferbase_complex,
1226            dataptr: dataptr($crate::Rcomplex),
1227            default: materializing($crate::Rcomplex)
1228            $(, $knob)*
1229        );
1230    };
1231}
1232// endregion