Skip to main content

miniextendr_macros/
lib.rs

1//! # miniextendr-macros - Procedural macros for Rust <-> R interop
2//!
3//! This crate provides the procedural macros that power miniextendr's code
4//! generation. Most users should depend on `miniextendr-api` and use its
5//! re-exports, but this crate can be used directly when you only need macros.
6//!
7//! Primary macros and derives:
8//! - `#[miniextendr]` on functions, impl blocks, trait defs, and trait impls.
9//! - `#[r_ffi_checked]` for main-thread routing of C-ABI wrappers.
10//! - Derives: `ExternalPtr`, `RNativeType`, ALTREP derives, `RFactor`.
11//! - Helpers: `typed_list` for typed list builders.
12//!
13//! R wrapper generation is driven by Rust doc comments (roxygen tags are
14//! extracted). During package build, the wrapper-gen pass loads the installed
15//! shared object into R and calls `miniextendr_write_wrappers`, which walks the
16//! linkme `#[distributed_slice]` tables and writes `R/<pkg>-wrappers.R`.
17//!
18//! ## Quick start
19//!
20//! ```ignore
21//! use miniextendr_api::miniextendr;
22//!
23//! #[miniextendr]
24//! fn add(a: i32, b: i32) -> i32 {
25//!     a + b
26//! }
27//! ```
28//!
29//! ## Macro expansion pipeline
30//!
31//! ### Overview
32//!
33//! ```text
34//! ┌──────────────────────────────────────────────────────────────────────────┐
35//! │                         #[miniextendr] on fn                             │
36//! │                                                                          │
37//! │  1. Parse: syn::ItemFn → MiniextendrFunctionParsed                       │
38//! │  2. Analyze return type (Result<T>, Option<T>, raw SEXP, etc.)           │
39//! │  3. Generate:                                                            │
40//! │     ├── C wrapper: extern "C-unwind" fn C_<crate>_<name>(...) → SEXP     │
41//! │     ├── R wrapper: static R_WRAPPER_<name>: RWrapperEntry                  │
42//! │     └── Registration: const call_method_def_<name>: R_CallMethodDef      │
43//! │  4. Original function preserved (with added attributes)                  │
44//! └──────────────────────────────────────────────────────────────────────────┘
45//!
46//! ┌──────────────────────────────────────────────────────────────────────────┐
47//! │                    #[miniextendr(env|r6|s3|s4|s7)] on impl               │
48//! │                                                                          │
49//! │  1. Parse: syn::ItemImpl → extract methods                               │
50//! │  2. For each method:                                                     │
51//! │     ├── Generate C wrapper (handles self parameter)                      │
52//! │     ├── Generate R method wrapper string                                 │
53//! │     └── Generate registration entry                                      │
54//! │  3. Generate class definition code per class system:                     │
55//! │     ├── env: new.env() + method assignment                               │
56//! │     ├── r6: R6Class() definition                                         │
57//! │     ├── s3: S3 generics + methods                                        │
58//! │     ├── s4: setClass() + setMethod()                                     │
59//! │     └── s7: new_class() definition                                       │
60//! │  4. Emit const with combined R code                                      │
61//! └──────────────────────────────────────────────────────────────────────────┘
62//!
63//! ┌──────────────────────────────────────────────────────────────────────────┐
64//! │                         #[miniextendr] on trait                          │
65//! │                                                                          │
66//! │  1. Parse: syn::ItemTrait → extract method signatures                    │
67//! │  2. Generate:                                                            │
68//! │     ├── Trait tag constant: const TAG_<TRAIT>: mx_tag = ...              │
69//! │     ├── Vtable struct: struct __vtable_<Trait> { ... }                   │
70//! │     └── CCalls table: static MX_CCALL_<TRAIT>: [...] = ...               │
71//! │  3. Original trait preserved                                             │
72//! └──────────────────────────────────────────────────────────────────────────┘
73//!
74//! ┌──────────────────────────────────────────────────────────────────────────┐
75//! │                    #[miniextendr] impl Trait for Type                    │
76//! │                                                                          │
77//! │  1. Parse: syn::ItemImpl (trait impl)                                    │
78//! │  2. Generate:                                                            │
79//! │     ├── Vtable instance: static __VTABLE_<crate>_<Trait>_FOR_<Type>     │
80//! │     ├── Shared runtime storage: ErasedObject<Type>                     │
81//! │     ├── Shared query: registry::universal_query                        │
82//! │     └── Base vtable: static __MX_BASE_VTABLE_<Type>: ...                 │
83//! │  3. Original impl preserved                                              │
84//! └──────────────────────────────────────────────────────────────────────────┘
85//!
86//! ```
87//!
88//! ### Key Modules
89//!
90//! | Module | Purpose |
91//! |--------|---------|
92//! | `miniextendr_fn` | Function parsing and attribute handling |
93//! | `c_wrapper_builder` | C wrapper generation (`extern "C-unwind"`) |
94//! | `r_wrapper_builder` | R wrapper code generation |
95//! | `rust_conversion_builder` | Rust→SEXP return value conversion |
96//! | `miniextendr_impl` | `impl Type` block processing |
97//! | `r_class_formatter` | Class system code generation (env/r6/s3/s4/s7) |
98//! | `miniextendr_trait` | Trait ABI metadata generation |
99//! | `miniextendr_impl_trait` | `impl Trait for Type` vtable generation |
100//! | `altrep` / `altrep_derive` | ALTREP struct derivation |
101//! | `externalptr_derive` | `#[derive(ExternalPtr)]` |
102//! | `roxygen` | Roxygen doc comment handling |
103//!
104//! ### Generated Symbol Naming
105//!
106//! Every `#[no_mangle]` symbol is prefixed with the consuming crate's name
107//! (from `CARGO_CRATE_NAME`) so two packages loaded into one webR session
108//! can't collide on a C symbol (#1273; helpers in `naming.rs`). For a
109//! function `my_func` in a crate `mypkg`:
110//! - C wrapper: `C_mypkg_my_func`
111//! - R wrapper registration: `R_WRAPPER_my_func`
112//! - Registration: `call_method_def_my_func`
113//!
114//! For a type `MyType` with trait `Counter` in a crate `mypkg`:
115//! - Vtable: `__VTABLE_mypkg_Counter_FOR_MyType`
116//! - Storage: runtime `ErasedObject<MyType>`
117//! - Query: `__mx_query_mytype`
118//!
119//! ## Return Type Handling
120//!
121//! The `return_type_analysis` module determines how to convert Rust returns to SEXP:
122//!
123//! | Rust Type | Strategy | R Result |
124//! |-----------|----------|----------|
125//! | `T: IntoR` | `.into_sexp()` | Converted value |
126//! | `Result<T, E>` | Unwrap or R error | Value or error |
127//! | `Option<T>` | `Some` → value, `None` → `NULL` | Value or NULL |
128//! | `SEXP` | Pass through | Raw SEXP |
129//! | `()` | Invisible NULL | `invisible(NULL)` |
130//!
131//! Use `#[miniextendr(unwrap_in_r)]` to return `Result<T, E>` to R without unwrapping.
132//!
133//! ## Thread Strategy
134//!
135//! By default, `#[miniextendr]` functions run on R's main thread. Opt into
136//! worker-thread execution with `#[miniextendr(worker)]` (requires the
137//! `worker-thread` feature on `miniextendr-api`). A worker opt-in is ignored
138//! when the signature requires main-thread execution (returns/takes `SEXP`,
139//! uses variadic dots, or sets `check_interrupt`).
140//!
141//! **Note**: `ExternalPtr<T>` is `Send` — it can be returned from worker
142//! thread functions. All R API operations on `ExternalPtr` are serialized
143//! through `with_r_thread`.
144//!
145//! ## Class Systems
146//!
147//! The `r_class_formatter` module generates R code for different class systems:
148//!
149//! | System | Generated R Code | Self Parameter |
150//! |--------|------------------|----------------|
151//! | `env` | `new.env()` with methods | `self` environment |
152//! | `r6` | `R6Class()` | `self` environment |
153//! | `s3` | `structure()` + generics | First argument |
154//! | `s4` | `setClass()` + `setMethod()` | First argument |
155//! | `s7` | `new_class()` | `self` property |
156
157// miniextendr-macros procedural macros
158
159mod altrep;
160mod c_wrapper_builder;
161mod call_method_def;
162mod cfg_gates;
163mod deprecation;
164mod dots_policy;
165mod externalptr_args;
166mod list_macro;
167mod match_arg_keys;
168mod miniextendr_fn;
169mod receiver_body;
170mod return_wrap;
171mod s7_conversion;
172mod trait_receiver;
173mod trait_self_ref;
174mod trait_signature;
175mod type_inspect;
176mod typed_dataframe;
177mod typed_list;
178mod util;
179use crate::miniextendr_fn::{MiniextendrFnAttrs, MiniextendrFunctionParsed};
180mod miniextendr_impl;
181mod r_wrapper_builder;
182/// Builder utilities for formatting R wrapper arguments and calls.
183pub(crate) use r_wrapper_builder::RArgumentBuilder;
184mod rust_conversion_builder;
185/// Helper for generating Rust→R conversion code for return values.
186pub(crate) use rust_conversion_builder::RustConversionBuilder;
187mod method_return_builder;
188/// Helpers for shaping method return handling (R vs Rust wrapper code).
189pub(crate) use method_return_builder::{MethodReturnBuilder, ReturnStrategy};
190mod altrep_derive;
191mod condition_derive;
192mod dataframe_derive;
193mod lifecycle;
194mod list_derive;
195mod r_class_formatter;
196mod r_preconditions;
197mod r_shadowing;
198mod return_type_analysis;
199mod roxygen;
200
201// Trait ABI support modules
202mod all_equal_derive;
203mod externalptr_derive;
204mod miniextendr_impl_trait;
205mod miniextendr_trait;
206mod typed_external_macro;
207
208// Factor support
209mod factor_derive;
210mod match_arg_derive;
211mod newtype_derive;
212
213// Struct/enum dispatch for #[miniextendr] on structs and enums
214mod struct_enum_dispatch;
215// Its fallback's diagnostic for a Rust `...` parameter that is not last
216mod misplaced_dots;
217
218// r! proc-macro implementation
219mod r_macro;
220
221// reexport! proc-macro implementation
222mod reexport;
223
224// vctrs support
225#[cfg(feature = "vctrs")]
226mod vctrs_derive;
227mod vctrs_generics;
228
229mod crate_config;
230mod naming;
231pub(crate) use naming::r_wrapper_const_ident_for;
232
233// Feature default mutual exclusivity guards
234#[cfg(all(feature = "r6-default", feature = "s7-default"))]
235compile_error!(
236    "features \"r6-default\" and \"s7-default\" are mutually exclusive — \
237     enable exactly one, or omit both to fall back to the unspecified default"
238);
239// Note: default-main-thread was removed — main thread is now the hardcoded default.
240// worker-default still opts into worker thread execution.
241
242pub(crate) use type_inspect::{
243    SeveralOkContainer, classify_several_ok_container, first_type_argument,
244    is_main_thread_bound_input, is_main_thread_bound_return, is_sexp_type, match_arg_choices_ty,
245    second_type_argument,
246};
247pub(crate) use util::{extract_cfg_attrs, r_wrapper_raw_literal, source_location_doc};
248
249/// Validate the signature of an `extern "C-unwind"` fn exported via `#[miniextendr]`.
250///
251/// R's `.Call` interface passes all arguments as `SEXP` and expects a `SEXP`
252/// return value. For `extern "C-unwind"` functions the user writes the C symbol
253/// directly, so the signature must satisfy those invariants statically —
254/// otherwise the generated registration produces UB at runtime.
255///
256/// Called before any codegen so we fail fast on an invalid extern signature
257/// rather than emitting a wrapper that would only matter after the error.
258fn validate_extern_signature(
259    abi: &syn::Abi,
260    attrs: &[syn::Attribute],
261    inputs: &syn::punctuated::Punctuated<syn::FnArg, ::syn::token::Commasyn::Token![,]>,
262    output: &syn::ReturnType,
263) -> syn::Result<()> {
264    use syn::spanned::Spanned;
265
266    // Reject `self` receivers up front — they are never valid for `.Call`
267    // exports, and a missing-return-type error would only hide the real
268    // problem (users write `fn foo(self)` to ask "can I export a method?").
269    for input in inputs.iter() {
270        if let syn::FnArg::Receiver(recv) = input {
271            return Err(syn::Error::new_spanned(
272                recv,
273                "self parameter not allowed in standalone functions; \
274                 use #[miniextendr(env|r6|s3|s4|s7)] on impl blocks instead",
275            ));
276        }
277    }
278
279    // Require one of #[no_mangle] / #[unsafe(no_mangle)] / #[export_name].
280    let has_no_mangle = attrs.iter().any(|attr| {
281        attr.path().is_ident("no_mangle")
282            || attr
283                .parse_nested_meta(|meta| {
284                    if meta.path.is_ident("no_mangle") {
285                        Err(meta.error("found #[no_mangle]"))
286                    } else {
287                        Ok(())
288                    }
289                })
290                .is_err()
291    });
292    let has_export_name = attrs.iter().any(|attr| attr.path().is_ident("export_name"));
293    if !has_no_mangle && !has_export_name {
294        return Err(syn::Error::new(
295            attrs
296                .first()
297                .map(|attr| attr.span())
298                .unwrap_or_else(|| abi.span()),
299            "extern \"C-unwind\" functions need a visible C symbol for R's .Call interface. \
300             Add one of:\n  \
301             - `#[unsafe(no_mangle)]` (Rust 2024 edition)\n  \
302             - `#[no_mangle]` (Rust 2021 edition)\n  \
303             - `#[export_name = \"my_symbol\"]` (custom symbol name)",
304        ));
305    }
306
307    // Return type must be SEXP.
308    match output {
309        non_return_type @ syn::ReturnType::Default => {
310            return Err(syn::Error::new(
311                non_return_type.span(),
312                "extern \"C-unwind\" functions used with #[miniextendr] must return SEXP. \
313                 Add `-> miniextendr_api::SEXP` as the return type. \
314                 If you want automatic type conversion, remove `extern \"C-unwind\"` and let \
315                 the macro generate the C wrapper.",
316            ));
317        }
318        syn::ReturnType::Type(_rarrow, output_type) => match output_type.as_ref() {
319            syn::Type::Path(type_path) => {
320                if let Some(path_to_sexp) = type_path.path.segments.last().map(|x| &x.ident)
321                    && path_to_sexp != "SEXP"
322                {
323                    return Err(syn::Error::new(
324                        path_to_sexp.span(),
325                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("extern \"C-unwind\" functions must return SEXP, found `{0}`. R\'s .Call interface expects SEXP return values. Change the return type to `miniextendr_api::SEXP`, or remove `extern \"C-unwind\"` to let the macro handle type conversion.",
                path_to_sexp))
    })format!(
326                            "extern \"C-unwind\" functions must return SEXP, found `{path_to_sexp}`. \
327                             R's .Call interface expects SEXP return values. \
328                             Change the return type to `miniextendr_api::SEXP`, or remove \
329                             `extern \"C-unwind\"` to let the macro handle type conversion.",
330                        ),
331                    ));
332                }
333            }
334            _ => {
335                return Err(syn::Error::new(
336                    output_type.span(),
337                    "extern \"C-unwind\" functions must return SEXP. \
338                     R's .Call interface expects SEXP return values. \
339                     Change the return type to `miniextendr_api::SEXP`, or remove \
340                     `extern \"C-unwind\"` to let the macro handle type conversion.",
341                ));
342            }
343        },
344    }
345
346    // Every input must be SEXP. Reject variadics and receivers.
347    for input in inputs.iter() {
348        match input {
349            syn::FnArg::Receiver(recv) => {
350                return Err(syn::Error::new_spanned(
351                    recv,
352                    "extern \"C-unwind\" functions cannot have a `self` parameter. \
353                     R's .Call interface only accepts SEXP arguments. \
354                     Use `#[miniextendr(env|r6|s3|s4|s7)]` on an impl block for methods.",
355                ));
356            }
357            syn::FnArg::Typed(pat_type) => {
358                if let syn::Pat::Rest(_) = pat_type.pat.as_ref() {
359                    return Err(syn::Error::new_spanned(
360                        pat_type,
361                        "extern functions cannot use variadic (...) - .Call passes fixed arguments",
362                    ));
363                }
364                let is_sexp = match pat_type.ty.as_ref() {
365                    syn::Type::Path(type_path) => type_path
366                        .path
367                        .segments
368                        .last()
369                        .is_some_and(|seg| seg.ident == "SEXP"),
370                    _ => false,
371                };
372                if !is_sexp && crate::type_inspect::is_lazy_dots_marker(&pat_type.ty) {
373                    return Err(syn::Error::new_spanned(
374                        &pat_type.ty,
375                        "extern functions cannot take `LazyDots`: it is the generated R \
376                         wrapper's frame (`environment()`), and `.Call()` reaches an extern \
377                         function directly. Drop the `extern \"C-unwind\"` to let the macro \
378                         generate the wrapper",
379                    ));
380                }
381                if !is_sexp {
382                    let is_dots_type = if let syn::Type::Reference(type_ref) = pat_type.ty.as_ref()
383                    {
384                        if let syn::Type::Path(inner) = type_ref.elem.as_ref() {
385                            inner
386                                .path
387                                .segments
388                                .last()
389                                .is_some_and(|seg| seg.ident == "Dots")
390                        } else {
391                            false
392                        }
393                    } else if let syn::Type::Path(type_path) = pat_type.ty.as_ref() {
394                        type_path
395                            .path
396                            .segments
397                            .last()
398                            .is_some_and(|seg| seg.ident == "Dots")
399                    } else {
400                        false
401                    };
402                    let msg = if is_dots_type {
403                        "extern functions cannot use Dots; use `...` syntax in non-extern #[miniextendr] functions instead"
404                    } else {
405                        "extern function parameters must be SEXP - .Call passes all arguments as SEXP"
406                    };
407                    return Err(syn::Error::new_spanned(&pat_type.ty, msg));
408                }
409            }
410        }
411    }
412
413    Ok(())
414}
415
416/// Builds the `let dots_typed = <dots>.typed(<spec>)...` statement injected
417/// at the start of a function body when `#[miniextendr(dots =
418/// typed_list!(...))]` is used.
419///
420/// Raises through `arg_error!` so validation keeps the argument-condition
421/// kind, the R parameter name (`...`), and the crate's configured classes.
422/// Display formatting preserves TypedListError's human-readable diagnostics.
423pub(crate) fn build_dots_validation_stmt(
424    dots_param: &syn::Ident,
425    spec_tokens: &proc_macro2::TokenStream,
426) -> syn::Stmt {
427    ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_ident(&mut _s, "let");
        ::quote::__private::push_ident(&mut _s, "dots_typed");
        ::quote::__private::push_eq(&mut _s);
        ::quote::ToTokens::to_tokens(&dots_param, &mut _s);
        ::quote::__private::push_dot(&mut _s);
        ::quote::__private::push_ident(&mut _s, "typed");
        ::quote::__private::push_group(&mut _s,
            ::quote::__private::Delimiter::Parenthesis,
            {
                let mut _s = ::quote::__private::TokenStream::new();
                ::quote::ToTokens::to_tokens(&spec_tokens, &mut _s);
                _s
            });
        ::quote::__private::push_dot(&mut _s);
        ::quote::__private::push_ident(&mut _s, "unwrap_or_else");
        ::quote::__private::push_group(&mut _s,
            ::quote::__private::Delimiter::Parenthesis,
            {
                let mut _s = ::quote::__private::TokenStream::new();
                ::quote::__private::push_or(&mut _s);
                ::quote::__private::push_ident(&mut _s, "e");
                ::quote::__private::push_or(&mut _s);
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "arg_error");
                ::quote::__private::push_bang(&mut _s);
                ::quote::__private::push_group(&mut _s,
                    ::quote::__private::Delimiter::Parenthesis,
                    {
                        let mut _s = ::quote::__private::TokenStream::new();
                        ::quote::__private::push_ident(&mut _s, "param");
                        ::quote::__private::push_eq(&mut _s);
                        ::quote::__private::parse(&mut _s, "\"...\"");
                        ::quote::__private::push_comma(&mut _s);
                        ::quote::__private::parse(&mut _s,
                            "\"dots validation failed: {e}\"");
                        _s
                    });
                _s
            });
        ::quote::__private::push_semi(&mut _s);
        _s
    });syn::parse_quote! {
428        let dots_typed = #dots_param.typed(#spec_tokens)
429            .unwrap_or_else(|e| ::miniextendr_api::arg_error!(
430                param = "...", "dots validation failed: {e}"
431            ));
432    }
433}
434
435/// Export Rust items to R.
436///
437/// `#[miniextendr]` can be applied to:
438/// - `fn` items (generate C + R wrappers)
439/// - `impl` blocks (generate R class methods)
440/// - `trait` items (generate trait ABI metadata)
441/// - ALTREP wrapper structs (generate `RegisterAltrep` impls)
442///
443/// # Functions
444///
445/// ```ignore
446/// use miniextendr_api::miniextendr;
447///
448/// #[miniextendr]
449/// fn add(a: i32, b: i32) -> i32 { a + b }
450/// ```
451///
452/// This produces a C wrapper `C_<crate>_add` and an R wrapper `add()`.
453/// Registration is automatic via linkme distributed slices.
454///
455/// ## `extern "C-unwind"`
456///
457/// If the function is declared `extern "C-unwind"` and exported with
458/// `#[no_mangle]` (2021), `#[unsafe(no_mangle)]` (2024), or `#[export_name = "..."]`,
459/// the function itself is the C symbol and the R wrapper is prefixed with
460/// `unsafe_` to signal bypassed safety (no worker isolation or conversion).
461///
462/// ## Variadics (`...`)
463///
464/// Use `name: ...` as the last argument. It becomes a `name: &Dots`
465/// parameter (`args: ...` → `args: &Dots`), through which the body reads
466/// the dots. For dots the function ignores, write `_: ...`. rustc rejects a
467/// bare `...` before the macro runs (the deny-by-default
468/// `varargs_without_pattern` lint) and suggests `_: ...`. The R formal is
469/// plain `...` either way.
470///
471/// An explicit `rest: &Dots` parameter is R's `...` too, at any position:
472/// `fn f(x: i32, rest: &Dots, overwrite: bool)` becomes
473/// `f <- function(x, ..., overwrite)`, and R matches a formal after `...` by
474/// its exact name only. A function takes at most one `...`.
475///
476/// ### Typed Dots Validation
477///
478/// Use `#[miniextendr(dots = typed_list!(...))]` to automatically validate dots
479/// and create a `dots_typed` variable with typed accessors:
480///
481/// ```ignore
482/// #[miniextendr(dots = typed_list!(x => numeric(), y => integer(), z? => character()))]
483/// pub fn my_func(_: ...) -> String {
484///     let x: f64 = dots_typed.get("x").expect("x");
485///     let y: i32 = dots_typed.get("y").expect("y");
486///     let z: Option<String> = dots_typed.get_opt("z").expect("z");
487///     format!("x={}, y={}", x, y)
488/// }
489/// ```
490///
491/// Type specs: `numeric()`, `integer()`, `logical()`, `character()`, `list()`,
492/// `raw()`, `complex()`, or `"class_name"` for class inheritance checks.
493/// Add `(n)` for exact length: `numeric(4)`. Use `?` suffix for optional fields.
494/// Use `@exact;` prefix for strict mode (reject extra fields).
495///
496/// ## Attributes
497///
498/// - `#[miniextendr(worker)]` — opt into worker-thread execution
499/// - `#[miniextendr(invisible)]` / `#[miniextendr(visible)]` — control return visibility
500///   (identical to an `Invisible<T>` / `Visible<T>` return type; the two must agree).
501///   Only unit-`NULL` returns are invisible by default; receiver-returning
502///   method tails are visible unless marked (#1213).
503/// - `#[miniextendr(check_interrupt)]` — check for a user interrupt once, before the
504///   arguments are converted and the body runs
505/// - `#[miniextendr(coerce)]` — coerce R type before conversion (also usable per-parameter)
506/// - `#[miniextendr(rng)]` — load R's RNG state before the body and save it after;
507///   a `&RngGuard` parameter (no R argument) receives the guard to draw from
508/// - `#[miniextendr(rng(seed = <param>))]` — `rng`, plus the seed contract of
509///   `stats::simulate()` in the R wrapper (#2198), for standalone functions and
510///   `s3(...)` methods. `<param>` names the seed parameter by its Rust name.
511///   After the argument checks the wrapper creates `.Random.seed` if it is
512///   missing; with a non-`NULL` seed it calls `set.seed(seed)` (which validates
513///   it, so the parameter has no R-side type check) and puts the session's
514///   `.Random.seed` back on exit, also after an error or a panic. The result
515///   gets a `seed` attribute: the seed with `kind = as.list(RNGkind())`, or the
516///   pre-draw `.Random.seed` for `seed = NULL`; a value that can't carry
517///   attributes is left as it is. A unit return, an impl or trait method, and a
518///   name that is no evaluated R argument are compile errors.
519/// - `#[miniextendr(serialize)]` — serialize the complete return through `AsSerialize<T>`
520///   (requires the API `serde` feature); composes with return visibility markers
521/// - `#[miniextendr(strict)]` — reject lossy conversions for i64/u64/isize/usize
522/// - `#[miniextendr(unwrap_in_r)]` — return `Result<T, E>` to R without unwrapping
523/// - `#[miniextendr(serde_error(tag = "..", prefix = "..", skip(..), rename(a = ".."))]` —
524///   options for the serde-classed `Err` arm. The path itself is automatic under the
525///   API crate's `serde` feature for every `Result<T, E>` with `E: Serialize + Display`.
526///   `as_character("a", ..)`, `as_integer(..)`, `as_double(..)` and `as_logical(..)` give
527///   payload fields that atomic R type, as the `serde::as_*` field helpers would from the
528///   error type itself; like `skip` and `rename` they name fields as the error serializes
529///   them, before any rename. A field takes one `as_*` option and is not also skipped
530///   (compile errors, all reported at once). A
531///   `miniextendr_api::condition::SerdeErrorShape` const holds any of these options;
532///   `miniextendr_api::serde_error_shape!(Error = ENGINE)` declares it once for an error
533///   type, and every function or method of the crate returning that type applies it
534///   first, with or without this attribute. `shape = PATH` names another one per
535///   function: `serde_error(shape = STRICT, as_double("n"))` applies the declared shape,
536///   then `STRICT`, then the function's own options; the later wins for a field set more
537///   than once. The path resolves from the function's module (`Self::X` on a method
538///   names the impl's type); a misspelt one is a compile error.
539/// - `#[miniextendr(dots = typed_list!(...))]` — validate dots, create `dots_typed`
540/// - `#[miniextendr(dots = empty | empty(allow = [..]) | forward(allow = [..]) | data)]` —
541///   what `...` takes; the R wrapper refuses the other arguments before
542///   evaluating any, with a "did you mean" suggestion
543/// - `#[miniextendr(internal)]` — adds `@keywords internal` to R wrapper
544/// - `#[miniextendr(noexport)]` — suppresses `@export` from R wrapper
545///
546/// ## Declared R formals (`formal`)
547///
548/// A parameter's R formal is its name without leading underscores. For an R
549/// name that is no Rust identifier (`row.names`, `na.rm`, `.data`), declare it
550/// with `formal`, on the parameter of a function or with `formal(p = "..")` on
551/// an impl method. It combines with the other per-parameter options:
552///
553/// ```ignore
554/// #[miniextendr(s3(generic = "as.data.frame", class = "fit"))]
555/// pub fn as_data_frame_fit(
556///     x: List,
557///     #[miniextendr(formal = "row.names", default = "NULL")] row_names: Option<String>,
558///     #[miniextendr(default = "FALSE")] optional: bool,
559///     _dots: ...
560/// ) -> BuiltDataFrame { /* ... */ }
561/// // as.data.frame.fit <- function(x, row.names = NULL, optional = FALSE, ...)
562///
563/// #[miniextendr(r6)]
564/// impl Tally {
565///     #[miniextendr(formal(na_rm = "na.rm"), defaults(na_rm = "FALSE"))]
566///     pub fn add(&mut self, by: f64, na_rm: bool) -> f64 { /* ... */ }
567/// }
568/// ```
569///
570/// Every R-facing name uses the formal: the usage, the `.Call()` arguments,
571/// the argument checks and their `e$param` (a failed conversion's too), the
572/// `match_arg` prelude and the generated `@param` lines; the method-level
573/// options keep the Rust name as their key. The formal must be a syntactic R
574/// name, not a reserved word, `...` or `..1`, not a name the generated wrapper
575/// uses itself (`.call`, `.val`, `.ptr`, the S3 dispatch variables, names
576/// starting `.mx_`, `.miniextendr_`, `.rdata_` or `.__MX_`, a method's
577/// receiver), and distinct from the other formals. The dots, an `NArgs`
578/// parameter, S7 property accessors and trait methods take no `formal`.
579///
580/// # Impl blocks (class systems)
581///
582/// Apply `#[miniextendr(env|r6|s7|s3|s4)]` to an `impl Type` block.
583/// A type's methods can be split over several impl blocks: they are merged into
584/// one class. One block has the constructor and the class doc comment, and
585/// every block repeats the class options.
586/// Registration is automatic.
587///
588/// ## R6 Active Bindings
589///
590/// For R6 classes, use `#[miniextendr(r6(active))]` on methods to create
591/// active bindings (computed properties accessed without parentheses):
592///
593/// ```ignore
594/// use miniextendr_api::miniextendr;
595///
596/// pub struct Rectangle {
597///     width: f64,
598///     height: f64,
599/// }
600///
601/// #[miniextendr(r6)]
602/// impl Rectangle {
603///     pub fn new(width: f64, height: f64) -> Self {
604///         Self { width, height }
605///     }
606///
607///     /// Returns the area (width * height).
608///     #[miniextendr(r6(active))]
609///     pub fn area(&self) -> f64 {
610///         self.width * self.height
611///     }
612///
613///     /// Regular method (requires parentheses).
614///     pub fn scale(&mut self, factor: f64) {
615///         self.width *= factor;
616///         self.height *= factor;
617///     }
618/// }
619/// ```
620///
621/// In R:
622/// ```r
623/// r <- Rectangle$new(3, 4)
624/// r$area        # 12 (active binding - no parentheses!)
625/// r$scale(2)    # Regular method call
626/// r$area        # 24
627/// ```
628///
629/// Active bindings must be getter-only methods taking only `&self`.
630///
631/// ## S7 Properties
632///
633/// For S7 classes, use `#[miniextendr(s7(getter))]` and `#[miniextendr(s7(setter))]`
634/// to create computed properties accessed via `@`:
635///
636/// ```ignore
637/// use miniextendr_api::{miniextendr, ExternalPtr};
638///
639/// #[derive(ExternalPtr)]
640/// pub struct Range {
641///     start: f64,
642///     end: f64,
643/// }
644///
645/// #[miniextendr(s7)]
646/// impl Range {
647///     pub fn new(start: f64, end: f64) -> Self {
648///         Self { start, end }
649///     }
650///
651///     /// Computed property (read-only): length of the range.
652///     #[miniextendr(s7(getter))]
653///     pub fn length(&self) -> f64 {
654///         self.end - self.start
655///     }
656///
657///     /// Dynamic property getter.
658///     #[miniextendr(s7(getter, prop = "midpoint"))]
659///     pub fn get_midpoint(&self) -> f64 {
660///         (self.start + self.end) / 2.0
661///     }
662///
663///     /// Dynamic property setter.
664///     #[miniextendr(s7(setter, prop = "midpoint"))]
665///     pub fn set_midpoint(&mut self, value: f64) {
666///         let half = self.length() / 2.0;
667///         self.start = value - half;
668///         self.end = value + half;
669///     }
670/// }
671/// ```
672///
673/// In R:
674/// ```r
675/// r <- Range(0, 10)
676/// r@length     # 10 (computed, read-only)
677/// r@midpoint   # 5 (dynamic property)
678/// r@midpoint <- 20  # Adjusts start/end to center at 20
679/// ```
680///
681/// ### Property Attributes
682///
683/// - `#[miniextendr(s7(getter))]` - Read-only computed property
684/// - `#[miniextendr(s7(getter, prop = "name"))]` - Named property getter
685/// - `#[miniextendr(s7(setter, prop = "name"))]` - Named property setter
686/// - `#[miniextendr(s7(getter, default = "0.0"))]` - Property with default value
687/// - `#[miniextendr(s7(getter, required))]` - Required property (error if not provided)
688/// - `#[miniextendr(s7(getter, frozen))]` - Property that can only be set once
689/// - `#[miniextendr(s7(getter, deprecated = "Use X instead"))]` - Deprecated property
690/// - `#[miniextendr(s7(validate))]` - Validator function for property
691///
692/// ## S7 Generic Dispatch Control
693///
694/// Control how S7 generics are created:
695///
696/// - `#[miniextendr(s7(no_dots))]` - Create strict generic without `...`
697/// - `#[miniextendr(s7(dispatch = "x, other"))]` - Multi-dispatch: the receiver
698///   (`x`), then the method's leading parameters (`other`), in order
699/// - `#[miniextendr(s7(fallback))]` - Register method for `class_any` (catch-all).
700///   The generated R wrapper uses `tryCatch(x@.ptr, error = function(e) x)` to
701///   safely extract the self argument, so non-miniextendr objects won't crash with
702///   a slot-access error. Instead, incompatible objects produce a Rust type-conversion
703///   error when the method tries to interpret the argument as `&Self`.
704///
705/// ```ignore
706/// #[miniextendr(s7)]
707/// impl MyClass {
708///     /// Strict generic: function(x) instead of function(x, ...)
709///     #[miniextendr(s7(no_dots))]
710///     pub fn strict_method(&self) -> i32 { 42 }
711///
712///     /// Fallback method dispatched on class_any.
713///     /// Calling this on a non-MyClass object produces a type-conversion error,
714///     /// not a slot-access crash.
715///     #[miniextendr(s7(fallback))]
716///     pub fn describe(&self) -> String { "generic description".into() }
717/// }
718/// ```
719///
720/// ## S7 Type Conversion (`convert`)
721///
722/// Use `convert_from` and `convert_to` to enable S7's `convert()` for type coercion:
723///
724/// ```ignore
725/// use miniextendr_api::{miniextendr, ExternalPtr};
726///
727/// #[derive(ExternalPtr)]
728/// pub struct Celsius { value: f64 }
729///
730/// #[derive(ExternalPtr)]
731/// pub struct Fahrenheit { value: f64 }
732///
733/// #[miniextendr(s7)]
734/// impl Fahrenheit {
735///     pub fn new(value: f64) -> Self { Self { value } }
736///
737///     /// Convert FROM Celsius TO Fahrenheit.
738///     /// Usage: S7::convert(celsius_obj, Fahrenheit)
739///     #[miniextendr(s7(convert_from = "Celsius"))]
740///     pub fn from_celsius(c: &Celsius) -> Self {
741///         Fahrenheit { value: c.value * 9.0 / 5.0 + 32.0 }
742///     }
743///
744///     /// Convert FROM Fahrenheit TO Celsius.
745///     /// Usage: S7::convert(fahrenheit_obj, Celsius)
746///     #[miniextendr(s7(convert_to = "Celsius"))]
747///     pub fn to_celsius(&self) -> Celsius {
748///         Celsius { value: (self.value - 32.0) * 5.0 / 9.0 }
749///     }
750/// }
751/// ```
752///
753/// In R:
754/// ```r
755/// c <- Celsius(100)
756/// f <- S7::convert(c, Fahrenheit)  # Uses convert_from
757/// c2 <- S7::convert(f, Celsius)    # Uses convert_to
758/// ```
759///
760/// **Note:** Classes must be defined before they can be referenced in convert methods.
761/// Define the "from" class before the "to" class to avoid forward reference issues.
762///
763/// # Traits (ABI)
764///
765/// Apply `#[miniextendr]` to a trait to generate ABI metadata, then use
766/// `#[miniextendr] impl Trait for Type`. Registration is automatic.
767///
768/// # ALTREP
769///
770/// `#[miniextendr]` no longer generates ALTREP classes — `class`/`base`
771/// attributes on a one-field struct are a compile error. Use the per-family
772/// derives (`#[derive(AltrepInteger)]`, …) with `#[altrep(class = "...")]`
773/// instead; registration is automatic there.
774#[proc_macro_attribute]
775pub fn miniextendr(
776    attr: proc_macro::TokenStream,
777    item: proc_macro::TokenStream,
778) -> proc_macro::TokenStream {
779    // Spans a failed earlier expansion left behind belong to no output.
780    let _ = deprecation::take();
781    let expanded = proc_macro2::TokenStream::from(expand_miniextendr(attr, item));
782    let warnings = deprecation::take();
783    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&expanded, &mut _s);
    ::quote::ToTokens::to_tokens(&warnings, &mut _s);
    _s
}quote::quote! { #expanded #warnings }.into()
784}
785
786/// The `#[miniextendr]` expansion, before the deprecated-option warnings
787/// ([`deprecation`]) are appended.
788fn expand_miniextendr(
789    attr: proc_macro::TokenStream,
790    item: proc_macro::TokenStream,
791) -> proc_macro::TokenStream {
792    // rustc must select gated members before any expansion reads them (#2107).
793    if let Ok(parsed) = syn::parse::<syn::Item>(item.clone()) {
794        match cfg_gates::select(&attr.clone().into(), &parsed) {
795            Ok(Some(selected)) => return selected.into(),
796            Ok(None) => {}
797            Err(error) => return error.into_compile_error().into(),
798        }
799    }
800
801    // Try to parse as function first
802    if syn::parse::<syn::ItemFn>(item.clone()).is_ok() {
803        expand_fn(attr.into(), item.into())
804    } else if syn::parse::<syn::ItemImpl>(item.clone()).is_ok() {
805        // Delegate to impl block parser
806        miniextendr_impl::expand_impl(attr, item)
807    } else if syn::parse::<syn::ItemTrait>(item.clone()).is_ok() {
808        // Delegate to trait ABI generator
809        miniextendr_trait::expand_trait(attr, item)
810    } else {
811        // Delegate to struct/enum dispatch (handles ALTREP, ExternalPtr, list, dataframe, factor, match_arg)
812        struct_enum_dispatch::expand_struct_or_enum(attr, item)
813    }
814}
815
816/// `#[miniextendr]` on a function. It reads `proc_macro2` tokens and returns
817/// the token type its caller asks for: `proc_macro`'s from the attribute, or
818/// `proc_macro2`'s in the unit tests, which run outside a proc macro.
819fn expand_fn<T: From<proc_macro2::TokenStream>>(
820    attr: proc_macro2::TokenStream,
821    item: proc_macro2::TokenStream,
822) -> T {
823    let MiniextendrFnAttrs {
824        force_worker,
825        force_invisible,
826        check_interrupt,
827        coerce_all,
828        rng,
829        rng_seed,
830        unwrap_in_r,
831        serialize,
832        wrap,
833        serde_error,
834        preconditions,
835        call_attribution: call_attribution_attr,
836        call_arg,
837        return_pref,
838        return_pref_span,
839        s3_generic,
840        s3_class,
841        dots_spec,
842        dots_policy,
843        dots_span,
844        lifecycle,
845        strict,
846        internal,
847        noexport,
848        export,
849        doc,
850        c_symbol,
851        r_name: fn_r_name,
852        postfix: fn_postfix,
853        r_entry,
854        r_post_checks,
855        r_on_exit,
856    } = match syn::parse2(attr) {
857        Ok(attrs) => attrs,
858        Err(err) => return err.into_compile_error().into(),
859    };
860
861    let mut parsed = match syn::parse2::<MiniextendrFunctionParsed>(item) {
862        Ok(parsed) => parsed,
863        Err(err) => return err.into_compile_error().into(),
864    };
865
866    // Reject async functions
867    if let Some(asyncness) = &parsed.item().sig.asyncness {
868        return syn::Error::new_spanned(
869            asyncness,
870            "async functions are not supported by #[miniextendr]; \
871             R's C API is synchronous and incompatible with async executors",
872        )
873        .into_compile_error()
874        .into();
875    }
876
877    // Validate: reject type/const generic functions.
878    // Lifetime params ARE allowed — they are erased at codegen and produce a single monomorphic
879    // symbol, so `#[no_mangle] extern "C-unwind" fn f<'a>(...)` is valid.
880    // Type and const params require monomorphization → multiple symbols → cannot be #[no_mangle].
881    {
882        let params = &parsed.item().sig.generics.params;
883        let has_type_or_const = params
884            .iter()
885            .any(|p| #[allow(non_exhaustive_omitted_patterns)] match p {
    syn::GenericParam::Type(_) | syn::GenericParam::Const(_) => true,
    _ => false,
}matches!(p, syn::GenericParam::Type(_) | syn::GenericParam::Const(_)));
886
887        if has_type_or_const {
888            let err = syn::Error::new_spanned(
889                &parsed.item().sig.generics,
890                "#[miniextendr] functions cannot have generic type or const parameters. \
891                 Generic functions are incompatible with `extern \"C-unwind\"` and `#[no_mangle]` \
892                 required for R FFI. Consider using trait objects or monomorphization instead. \
893                 Explicit lifetime parameters are allowed (lifetimes are erased at codegen).",
894            );
895            return err.into_compile_error().into();
896        }
897    }
898
899    // NB: no automatic `#[track_caller]` (dropped in #1121). It made a *direct*
900    // panic's hook-location resolve to the generated wrapper glue instead of the
901    // user's `panic!` line, which defeats the `(at file:line)` suffix now folded
902    // into the R error message from the panic hook. See docs/TRACK_CALLER.md.
903    parsed.add_inline_never_if_needed();
904
905    // Extract commonly used values
906    let uses_internal_c_wrapper = parsed.uses_internal_c_wrapper();
907    let c_ident = if let Some(ref sym) = c_symbol {
908        syn::Ident::new(sym, parsed.c_wrapper_ident().span())
909    } else {
910        parsed.c_wrapper_ident()
911    };
912    let r_wrapper_generator = parsed.r_wrapper_const_ident();
913
914    // Extract references to parsed components
915    let rust_ident = parsed.ident();
916    let all_inputs = parsed.inputs();
917    // Return-visibility markers (`Invisible<T>` / `Visible<T>`, #1213) are
918    // peeled here so every later analysis sees the inner type; the marker's
919    // decision is folded in below, next to the attribute form.
920    let (visibility_marker, peeled_output) = {
921        let output = parsed.output();
922        if let syn::ReturnType::Type(_, ty) = output
923            && let Some(err) = crate::type_inspect::visibility_marker_error(ty, "return")
924        {
925            return err.into_compile_error().into();
926        }
927        crate::type_inspect::peel_return_visibility(output)
928    };
929    let (return_wrap, peeled_output) = match crate::return_wrap::resolve(&peeled_output, wrap) {
930        Ok(resolved) => resolved,
931        Err(error) => return error.into_compile_error().into(),
932    };
933    if return_wrap
934        .as_ref()
935        .is_some_and(|wrap| wrap.conversion.is_some())
936    {
937        return syn::Error::new_spanned(
938            parsed.output(),
939            "ConvertTo/ConvertFrom require an inherent S7 impl method",
940        )
941        .into_compile_error()
942        .into();
943    }
944    if return_wrap.is_some() && (serialize || unwrap_in_r) {
945        return syn::Error::new_spanned(
946            parsed.output(),
947            "explicit class wrapping cannot be combined with serialize or unwrap_in_r",
948        )
949        .into_compile_error()
950        .into();
951    }
952    if return_wrap.is_some() && parsed.abi().is_some() {
953        return syn::Error::new_spanned(parsed.output(), "explicit class wrapping requires a generated Rust-to-R wrapper, not an extern function").into_compile_error().into();
954    }
955    let converted_output = crate::type_inspect::serialize_return_type(&peeled_output, serialize);
956    let output = &converted_output;
957    let abi = parsed.abi();
958    if serialize && abi.is_some() {
959        return syn::Error::new_spanned(
960            parsed.output(),
961            "`serialize` requires a generated Rust-to-R conversion and cannot be used on an extern function",
962        )
963        .into_compile_error()
964        .into();
965    }
966    let attrs = parsed.attrs();
967    let vis = parsed.vis();
968    let generics = parsed.generics();
969    let has_dots = parsed.has_dots();
970    let dots_ident = parsed.dots_ident().cloned();
971    // The R formal of each parameter (#1842): what every R-facing name below
972    // is written as.
973    let formals = parsed.formals();
974
975    // Fail fast on invalid extern "C-unwind" signatures *before* any codegen,
976    // so we never emit a wrapper that would be discarded by the surfaced error.
977    if let Some(user_abi) = abi
978        && let Err(e) = validate_extern_signature(user_abi, attrs, all_inputs, output)
979    {
980        return e.into_compile_error().into();
981    }
982
983    // Condition-call markers (#1566): a `Call` / `CallerCall` parameter is the
984    // type-level spelling of `call = wrapper` / `call = caller`. It is not an
985    // R formal: the C wrapper binds it from the hidden `__miniextendr_call`
986    // slot, so every R-side consumer below sees `inputs` with the marker
987    // removed while the Rust call site substitutes the slot.
988    let mut call_marker: Option<(syn::Ident, r_wrapper_builder::CallAttribution)> = None;
989    for arg in all_inputs.iter() {
990        let syn::FnArg::Typed(pt) = arg else {
991            continue;
992        };
993        let Some(kind) = crate::type_inspect::call_marker(&pt.ty) else {
994            continue;
995        };
996        let syn::Pat::Ident(pat_ident) = pt.pat.as_ref() else {
997            continue;
998        };
999        let marker = kind
1000            .marker_name()
1001            .expect("a marker type selects an attribution that has a marker");
1002        if let Some((first, _)) = &call_marker {
1003            return syn::Error::new_spanned(
1004                pt,
1005                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("a `#[miniextendr]` function takes at most one `Call` / `CallerCall` parameter (`{0}` already receives the condition call); the call slot is a single value",
                first))
    })format!(
1006                    "a `#[miniextendr]` function takes at most one `Call` / `CallerCall` \
1007                     parameter (`{first}` already receives the condition call); the call \
1008                     slot is a single value"
1009                ),
1010            )
1011            .into_compile_error()
1012            .into();
1013        }
1014        if parsed.has_param_attrs(&crate::naming::ident_name(&pat_ident.ident)) {
1015            return syn::Error::new_spanned(
1016                pt,
1017                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("per-parameter options (`coerce`, `match_arg`, `choices`, `default`, `several_ok`, `formal`) do not apply to a `{0}` parameter: it is bound from the call slot, not from an R argument",
                marker))
    })format!(
1018                    "per-parameter options (`coerce`, `match_arg`, `choices`, `default`, \
1019                     `several_ok`, `formal`) do not apply to a `{marker}` parameter: it is \
1020                     bound from the call slot, not from an R argument"
1021                ),
1022            )
1023            .into_compile_error()
1024            .into();
1025        }
1026        if kind == r_wrapper_builder::CallAttribution::Caller && !(noexport || internal) {
1027            return syn::Error::new_spanned(
1028                pt,
1029                "a `CallerCall` parameter attributes conditions to the wrapper's caller, which \
1030                 is only meaningful for a package-internal entry point; add `noexport` or \
1031                 `internal`.",
1032            )
1033            .into_compile_error()
1034            .into();
1035        }
1036        if kind == r_wrapper_builder::CallAttribution::Caller && call_arg {
1037            return syn::Error::new_spanned(
1038                pt,
1039                "a `CallerCall` parameter cannot be combined with `call_arg`: a `caller` wrapper \
1040                 already takes `.call`, where NULL means the caller's call. Keep the \
1041                 `CallerCall` parameter, or make it `Call` so that NULL means this function's \
1042                 own call.",
1043            )
1044            .into_compile_error()
1045            .into();
1046        }
1047        if call_attribution_attr == Some(r_wrapper_builder::CallAttribution::NoCall) {
1048            return syn::Error::new_spanned(
1049                pt,
1050                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`call = none` passes no call, so a `{0}` parameter has nothing to receive; drop the parameter or the option",
                marker))
    })format!(
1051                    "`call = none` passes no call, so a `{marker}` parameter has nothing to \
1052                     receive; drop the parameter or the option"
1053                ),
1054            )
1055            .into_compile_error()
1056            .into();
1057        }
1058        if let Some(attr) = call_attribution_attr
1059            && attr != kind
1060        {
1061            return syn::Error::new_spanned(
1062                pt,
1063                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("the `{3}` parameter selects `{0}` attribution but the attribute selects `{1}` (`call = {2}`); keep one of them (or make them agree)",
                kind.name(), attr.name(), attr.name(), marker))
    })format!(
1064                    "the `{marker}` parameter selects `{}` attribution but the attribute \
1065                     selects `{}` (`call = {}`); keep one of them (or make them agree)",
1066                    kind.name(),
1067                    attr.name(),
1068                    attr.name(),
1069                ),
1070            )
1071            .into_compile_error()
1072            .into();
1073        }
1074        call_marker = Some((pat_ident.ident.clone(), kind));
1075    }
1076    // Unevaluated-argument markers (#1835): a `Quoted` / `Quosure` parameter
1077    // stays an R formal, which the wrapper passes unevaluated
1078    // (`RArgumentBuilder::build_call_args_vec`). Every per-parameter option
1079    // would force it (a check, a coercion, a default evaluated in the wrapper's
1080    // frame), and a marker in any other position than the whole type or
1081    // `Missing<..>` would be converted as a forced value: all compile errors.
1082    // One exception: `default` alone on `Missing<..>` only writes the formal
1083    // (`x = NULL` in the usage). An omitted argument is still `missing()`, so
1084    // the wrapper passes the missing-argument sentinel and never evaluates the
1085    // default.
1086    let mut unevaluated_r_names: Vec<String> = Vec::new();
1087    for arg in all_inputs.iter() {
1088        let syn::FnArg::Typed(pt) = arg else {
1089            continue;
1090        };
1091        let Some(param) = crate::type_inspect::unevaluated_param(&pt.ty) else {
1092            if crate::type_inspect::mentions_unevaluated_marker(&pt.ty) {
1093                return syn::Error::new_spanned(
1094                    &pt.ty,
1095                    "`Quoted` / `Quosure` must be the parameter's whole type, or the type \
1096                     argument of `Missing<..>`: the wrapper passes the argument unevaluated \
1097                     only for those shapes",
1098                )
1099                .into_compile_error()
1100                .into();
1101            }
1102            continue;
1103        };
1104        let syn::Pat::Ident(pat_ident) = pt.pat.as_ref() else {
1105            continue;
1106        };
1107        let marker = param.kind.name();
1108        let rust_name = crate::naming::ident_name(&pat_ident.ident);
1109        // `formal` only names the argument (#1842), so it applies.
1110        if parsed
1111            .param_attrs(&rust_name)
1112            .is_some_and(|attrs| !attrs.leaves_argument_unforced(param.optional))
1113        {
1114            let hint = if param.optional {
1115                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`Missing<{0}>` takes `default` alone, which only writes the formal and is never evaluated",
                marker))
    })format!(
1116                    "`Missing<{marker}>` takes `default` alone, which only writes the \
1117                     formal and is never evaluated"
1118                )
1119            } else {
1120                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Use `Missing<{0}>` for an optional argument",
                marker))
    })format!("Use `Missing<{marker}>` for an optional argument")
1121            };
1122            return syn::Error::new_spanned(
1123                pt,
1124                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("per-parameter options (`coerce`, `match_arg`, `choices`, `several_ok`, `default`, `no_na`, `inherits`, `not_inherits`, `preconditions`) do not apply to a `{0}` parameter: the wrapper passes the argument unevaluated, and each of them would force it (`formal`, which only names it, does apply). {1}",
                marker, hint))
    })format!(
1125                    "per-parameter options (`coerce`, `match_arg`, `choices`, `several_ok`, \
1126                     `default`, `no_na`, `inherits`, `not_inherits`, `preconditions`) do not \
1127                     apply to a `{marker}` parameter: the wrapper passes the argument \
1128                     unevaluated, and each of them would force it (`formal`, which only \
1129                     names it, does apply). {hint}"
1130                ),
1131            )
1132            .into_compile_error()
1133            .into();
1134        }
1135        // `Checked<Quoted>` / `Unchecked<..>` is refused at parse time
1136        // (`MiniextendrFunctionParsed::parse`): no R-side type check to select.
1137        unevaluated_r_names.push(formals.of(&rust_name));
1138    }
1139    // The argument-count marker (#1860): an `NArgs` parameter is no R formal;
1140    // the wrapper passes `nargs()` at its position and the C wrapper converts
1141    // it like any argument, so it stays in `inputs`. `RArgumentBuilder`,
1142    // `r_formal_names`, the preconditions, the `@param` fillers and the
1143    // replacement-generic check each skip it.
1144    if let Err(err) =
1145        miniextendr_fn::check_nargs_params(all_inputs, |name| parsed.has_param_attrs(name))
1146    {
1147        return err.into_compile_error().into();
1148    }
1149    // The unforced dots (#1892): a `LazyDots` parameter is R's `...`, which
1150    // the wrapper passes as its frame; it takes no per-parameter option, and
1151    // any other shape (`&LazyDots`, `Option<..>`) is refused.
1152    if let Err(err) =
1153        miniextendr_fn::check_lazy_dots_params(all_inputs, |name| parsed.has_param_attrs(name))
1154    {
1155        return err.into_compile_error().into();
1156    }
1157    // The RNG guard (#2002): a `&RngGuard` parameter needs `rng`, whose
1158    // wrapper passes the guard it holds; it is no R formal either.
1159    if let Err(err) =
1160        miniextendr_fn::check_rng_guard_params(all_inputs, rng, |name| parsed.has_param_attrs(name))
1161    {
1162        return err.into_compile_error().into();
1163    }
1164    let has_rng_guard = all_inputs.iter().any(|arg| {
1165        #[allow(non_exhaustive_omitted_patterns)] match arg {
    syn::FnArg::Typed(pt) if crate::type_inspect::is_rng_guard_param(&pt.ty)
        => true,
    _ => false,
}matches!(arg, syn::FnArg::Typed(pt) if crate::type_inspect::is_rng_guard_param(&pt.ty))
1166    });
1167    let r_inputs: syn::punctuated::Punctuated<syn::FnArg, ::syn::token::Commasyn::Token![,]> = all_inputs
1168        .iter()
1169        .filter(|arg| match arg {
1170            syn::FnArg::Typed(pt) => {
1171                crate::type_inspect::call_marker(&pt.ty).is_none()
1172                    && !crate::type_inspect::is_rng_guard_param(&pt.ty)
1173            }
1174            syn::FnArg::Receiver(_) => true,
1175        })
1176        .cloned()
1177        .collect();
1178    let inputs = &r_inputs;
1179
1180    // A method for a replacement generic (`s3(generic = "$<-", ...)`) takes
1181    // the new value last, as `value` (#1853). Checked on the R formals, so a
1182    // trailing condition-call marker doesn't hide the parameter before it.
1183    if let Some(generic) = &s3_generic
1184        && let Err(err) = miniextendr_fn::check_replacement_value_param(
1185            crate::naming::split_generic(generic).1,
1186            inputs,
1187            &formals,
1188            rust_ident.span(),
1189        )
1190    {
1191        return err.into_compile_error().into();
1192    }
1193
1194    // Check for @title/@description conflicts with implicit values (doc-lint feature)
1195    // Skip when `doc` attribute overrides the roxygen — implicit docs are irrelevant then.
1196    let doc_lint_warnings = if doc.is_some() {
1197        proc_macro2::TokenStream::new()
1198    } else {
1199        crate::roxygen::doc_conflict_warnings(attrs, rust_ident.span())
1200    };
1201
1202    // Calling the Rust function: each R-backed parameter by name, a `Call` /
1203    // `CallerCall` marker from the C wrapper's `__miniextendr_call` slot (the
1204    // R wrapper's `.call = ...` argument, so a `CallerCall` sees `.mx_call`),
1205    // the RNG guard from the `rng` wrapper.
1206    let rust_inputs: Vec<proc_macro2::TokenStream> = all_inputs
1207        .iter()
1208        .filter_map(|arg| {
1209            if let syn::FnArg::Typed(pt) = arg
1210                && let syn::Pat::Ident(p) = pt.pat.as_ref()
1211            {
1212                let ident = &p.ident;
1213                if crate::type_inspect::is_rng_guard_param(&pt.ty) {
1214                    return Some(crate::type_inspect::rng_guard_arg());
1215                }
1216                return Some(match crate::type_inspect::call_marker(&pt.ty) {
1217                    Some(r_wrapper_builder::CallAttribution::Caller) => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "CallerCall");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "from_sexp");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "__miniextendr_call");
            _s
        });
    _s
}quote::quote! {
1218                        ::miniextendr_api::CallerCall::from_sexp(__miniextendr_call)
1219                    },
1220                    Some(_) => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "Call");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "from_sexp");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "__miniextendr_call");
            _s
        });
    _s
}quote::quote! {
1221                        ::miniextendr_api::Call::from_sexp(__miniextendr_call)
1222                    },
1223                    None => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&ident, &mut _s);
    _s
}quote::quote! { #ident },
1224                });
1225            }
1226            None
1227        })
1228        .collect();
1229
1230    // Validate dots_spec usage (actual injection happens later in the function body)
1231    if dots_spec.is_some() && !has_dots {
1232        let err = syn::Error::new(
1233            dots_span.unwrap_or_else(proc_macro2::Span::call_site),
1234            "#[miniextendr(dots = typed_list!(...))] requires a `...` or `&Dots` parameter in the function signature",
1235        );
1236        return err.into_compile_error().into();
1237    }
1238    if dots_spec.is_some()
1239        && let Some(err) = miniextendr_fn::typed_list_on_lazy_dots(all_inputs, dots_span)
1240    {
1241        return err.into_compile_error().into();
1242    }
1243    // `dots = empty | forward(..) | data` (#2157): a standalone wrapper's
1244    // `...` is the function's own dots parameter.
1245    if let Some(policy) = &dots_policy
1246        && let Err(err) = policy.check_signature(has_dots, has_dots)
1247    {
1248        return err.into_compile_error().into();
1249    }
1250
1251    // Analyze return type to determine:
1252    // - Whether it returns SEXP (affects thread strategy)
1253    // - Whether result should be invisible
1254    let rust_result_ident =
1255        syn::Ident::new("__miniextendr_rust_result", proc_macro2::Span::mixed_site());
1256    // `serde_error` only has an `Err` arm to act on when the fn returns `Result`.
1257    if serde_error.is_some() && !return_type_analysis::output_is_result(output) {
1258        return syn::Error::new_spanned(
1259            output,
1260            "`#[miniextendr(serde_error(..))]` requires a `Result<T, E>` return type: it classes \
1261             the condition raised from the `Err` arm",
1262        )
1263        .into_compile_error()
1264        .into();
1265    }
1266    let err_parts = c_wrapper_builder::ErrPartsMode::from_spec(serde_error.as_ref());
1267
1268    // Crate-level defaults (#1454, #1566): `[package.metadata.miniextendr]` in
1269    // the crate's Cargo.toml. Read once per expansion; a malformed table is
1270    // reported here, on the first `#[miniextendr]` function, for every key it
1271    // may carry.
1272    let crate_config = match crate::crate_config::crate_config() {
1273        Ok(config) => config,
1274        Err(err) => {
1275            return syn::Error::new(proc_macro2::Span::call_site(), err.to_string())
1276                .into_compile_error()
1277                .into();
1278        }
1279    };
1280
1281    // Resolve the R name once for both wrapper definitions and Rust-side errors.
1282    let is_s3_method = s3_generic.is_some() || s3_class.is_some();
1283    // Crate-level default for internal entry points (#1454):
1284    // `[package.metadata.miniextendr] noexport_postfix = "..."` applies to
1285    // every `noexport` / `internal` free function that names itself neither
1286    // via `r_name` nor via its own `postfix`. Exported functions and S3
1287    // methods keep their names. Precedence:
1288    // `r_name` > `postfix` > crate default > Rust name.
1289    let effective_postfix = match &fn_postfix {
1290        Some(postfix) => Some(postfix.clone()),
1291        None if (noexport || internal) && fn_r_name.is_none() && !is_s3_method => {
1292            crate_config.noexport_postfix.clone()
1293        }
1294        None => None,
1295    };
1296    let r_wrapper_ident_str: String;
1297    let s3_method_comment: String;
1298    // The registration tag of a method on a package-qualified generic (#2154).
1299    let mut s3_qualified_registration: Option<String> = None;
1300
1301    if is_s3_method {
1302        // For S3 methods, function name is generic.class
1303        // generic defaults to Rust function name if not specified
1304        let generic = s3_generic
1305            .clone()
1306            .unwrap_or_else(|| crate::naming::ident_name(rust_ident));
1307        let (package, generic) = crate::naming::split_generic(&generic);
1308        // s3_class is guaranteed to be Some here because MiniextendrFnAttrs::parse
1309        // validates that s3(...) always has class specified
1310        let class = s3_class.as_ref().expect("s3_class validated at parse time");
1311        r_wrapper_ident_str = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}.{1}", generic, class))
    })format!("{}.{}", generic, class);
1312        // Add @importFrom for vctrs generics so roxygen registers the dependency.
1313        // A qualified `vctrs::` generic registers lazily instead, so vctrs may
1314        // stay in Suggests.
1315        let import_comment =
1316            if package.is_none() && crate::vctrs_generics::is_vctrs_generic(generic) {
1317                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("#\' @importFrom vctrs {0}\n",
                generic))
    })format!("#' @importFrom vctrs {}\n", generic)
1318            } else {
1319                String::new()
1320            };
1321        s3_method_comment = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}#\' @method {1} {2}\n",
                import_comment, generic, class))
    })format!("{}#' @method {} {}\n", import_comment, generic, class);
1322        s3_qualified_registration =
1323            package.map(|package| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("#\' @exportS3Method {0}::{1} {2}\n",
                package, generic, class))
    })format!("#' @exportS3Method {package}::{generic} {class}\n"));
1324    } else if let Some(ref custom_name) = fn_r_name {
1325        r_wrapper_ident_str = custom_name.clone();
1326        s3_method_comment = String::new();
1327    } else if let Some(ref postfix) = effective_postfix {
1328        // `postfix = "_impl"` (or the crate default): the R wrapper is
1329        // `<rust_name><postfix>`; the C symbol keeps the Rust name.
1330        r_wrapper_ident_str = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}{1}",
                crate::naming::ident_name(rust_ident), postfix))
    })format!("{}{postfix}", crate::naming::ident_name(rust_ident));
1331        s3_method_comment = String::new();
1332    } else if abi.is_some() {
1333        r_wrapper_ident_str = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("unsafe_{0}",
                crate::naming::ident_name(rust_ident)))
    })format!("unsafe_{}", crate::naming::ident_name(rust_ident));
1334        s3_method_comment = String::new();
1335    } else {
1336        r_wrapper_ident_str = crate::naming::ident_name(rust_ident);
1337        s3_method_comment = String::new();
1338    };
1339
1340    let return_analysis = return_type_analysis::analyze_return_type(
1341        output,
1342        &rust_result_ident,
1343        &r_wrapper_ident_str,
1344        unwrap_in_r,
1345        strict,
1346        &err_parts,
1347    );
1348
1349    let returns_sexp = return_analysis.returns_sexp;
1350
1351    // Marker (`Invisible<T>` / `Visible<T>`) > attribute (`invisible` /
1352    // `visible`) > shape default (unit-`NULL` returns are invisible).
1353    let is_invisible_return_type = match crate::type_inspect::resolve_visibility(
1354        visibility_marker,
1355        force_invisible,
1356        return_analysis.is_invisible,
1357        syn::spanned::Spanned::span(parsed.output()),
1358    ) {
1359        Ok(v) => v,
1360        Err(err) => return err.into_compile_error().into(),
1361    };
1362
1363    // Check if any input parameter is main-thread-bound (SEXP or a !Send
1364    // framework wrapper like AltrepSexp — neither can move into the worker
1365    // closure, so the function must stay on the main thread). A `Call` /
1366    // `CallerCall` marker holds the call slot's SEXP and counts too.
1367    let has_sexp_inputs = all_inputs.iter().any(|arg| {
1368        if let syn::FnArg::Typed(pat_type) = arg {
1369            is_main_thread_bound_input(pat_type.ty.as_ref())
1370        } else {
1371            false
1372        }
1373    });
1374
1375    // Check if the return type is main-thread-bound (BuiltDataFrame /
1376    // DataFrameShape / an R-backed view — all hold R memory and are !Send, so
1377    // they cannot cross back from the worker thread; `run_on_worker` requires
1378    // `T: Send`). Walks nested positions: Result<BuiltDataFrame, E>,
1379    // Option<DataFrameShape>, Vec<...>, tuples.
1380    let has_main_thread_bound_return = match output {
1381        syn::ReturnType::Default => false,
1382        syn::ReturnType::Type(_, ty) => is_main_thread_bound_return(ty),
1383    };
1384
1385    // ═══════════════════════════════════════════════════════════════════════════
1386    // Thread Strategy Selection
1387    // ═══════════════════════════════════════════════════════════════════════════
1388    //
1389    // miniextendr supports two execution strategies:
1390    //
1391    // 1. **Main Thread Strategy** (with_r_unwind_protect) — DEFAULT
1392    //    - All code runs on R's main thread
1393    //    - Required when SEXP types are involved (not Send)
1394    //    - Required for R API calls (Rf_*, R_*)
1395    //    - Panic handling via R_UnwindProtect (Rust destructors run correctly)
1396    //    - Errors are always returned as tagged SEXP values; the R wrapper
1397    //      inspects the tag and raises a structured condition (`rust_*` class
1398    //      layering) past the Rust boundary.
1399    //    - Simpler execution model, better R integration
1400    //
1401    // 2. **Worker Thread Strategy** (run_on_worker + catch_unwind) — OPT-IN
1402    //    - Argument conversion on main thread (SEXP → Rust types)
1403    //    - Function execution on dedicated worker thread (clean panic isolation)
1404    //    - Result conversion on main thread (Rust types → SEXP)
1405    //    - Panic handling via catch_unwind (prevents unwinding across FFI boundary)
1406    //    - Opt in with #[miniextendr(worker)]
1407    //    - ExternalPtr<T> is Send: can be returned from worker thread functions
1408    //    - R API calls from worker use with_r_thread (serialized to main thread)
1409    //
1410    // Default: Main thread (simpler execution model, compatible with the
1411    // tagged-SEXP error transport)
1412    // Override: Use worker thread with #[miniextendr(worker)]
1413    //
1414    // Thread strategy:
1415    // - Main thread is always used unless force_worker is set
1416    // - force_worker cannot override hard requirements for main thread
1417    // - Hard requirements: returns_sexp, has_sexp_inputs (borrowed external
1418    //   arguments `&T` / `&mut T` / views included, #2106),
1419    //   has_main_thread_bound_return, has_dots, check_interrupt
1420    // A `Quoted` / `Quosure` parameter (also under `Missing<..>`, which
1421    // `is_main_thread_bound_input` does not look into) holds R objects and
1422    // evaluates R code (#1835). The RNG guard (#2002) is `!Send` and its
1423    // draws read R's RNG state.
1424    let requires_main_thread = returns_sexp
1425        || has_sexp_inputs
1426        || has_rng_guard
1427        || !unevaluated_r_names.is_empty()
1428        || has_main_thread_bound_return
1429        || has_dots
1430        || check_interrupt;
1431    let use_main_thread = !force_worker || requires_main_thread;
1432
1433    // Extract cfg attributes to apply to generated items (needed by CWrapperContext)
1434    let cfg_attrs = extract_cfg_attrs(parsed.attrs());
1435
1436    let source_loc_doc = source_location_doc(rust_ident.span());
1437
1438    // Build individual per-parameter coerce, match_arg_several_ok, layered
1439    // choice (a choice type wrapped in `Missing` / `Option`, #1473 / #1551) and
1440    // `no_na` lists (the value is checked after the conversion too)
1441    let mut coerce_params_list: Vec<String> = Vec::new();
1442    let mut match_arg_several_ok_params_list: Vec<String> = Vec::new();
1443    let mut layered_choice_params_list: Vec<(String, rust_conversion_builder::ChoiceLeaf)> =
1444        Vec::new();
1445    let mut no_na_params_list: Vec<(String, Option<String>)> = Vec::new();
1446    for input in inputs.iter() {
1447        if let syn::FnArg::Typed(pt) = input
1448            && let syn::Pat::Ident(pat_ident) = pt.pat.as_ref()
1449        {
1450            let param_name = crate::naming::ident_name(&pat_ident.ident);
1451            if parsed.has_coerce_attr(&param_name) {
1452                coerce_params_list.push(param_name.clone());
1453            }
1454            if let Some(attrs) = parsed.param_attrs(&param_name)
1455                && attrs.checks.no_na
1456            {
1457                no_na_params_list.push((param_name.clone(), attrs.checks.no_na_message.clone()));
1458            }
1459            if let Some(leaf) = parsed
1460                .param_attrs(&param_name)
1461                .and_then(miniextendr_fn::ParamAttrs::layered_leaf)
1462            {
1463                layered_choice_params_list.push((param_name, leaf));
1464            } else if parsed.has_match_arg_attr(&param_name) && parsed.has_several_ok(&param_name) {
1465                match_arg_several_ok_params_list.push(param_name);
1466            }
1467        }
1468    }
1469
1470    // Build the call expression: rust_ident(rust_input_1, rust_input_2, ...).
1471    // A visibility marker is a transparent newtype: unwrap it so the return
1472    // conversion works on the inner value (the analysis above saw the inner type).
1473    let fn_call_expr = crate::type_inspect::prepare_return_value(
1474        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&rust_ident, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            {
                use ::quote::__private::ext::*;
                let mut _first = true;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut rust_inputs, i) = rust_inputs.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let rust_inputs =
                        match rust_inputs.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&rust_inputs, &mut _s);
                }
            }
            _s
        });
    _s
}quote::quote! { #rust_ident(#(#rust_inputs),*) },
1475        visibility_marker,
1476        serialize,
1477    );
1478    let fn_call_expr = match &return_wrap {
1479        Some(wrap) => wrap.prepare_value(fn_call_expr),
1480        None => fn_call_expr,
1481    };
1482
1483    // Determine return handling: the rule methods share (`detect_return_handling`),
1484    // and unwrap_in_r (Result<T, E> → IntoR to pass result list to R).
1485    let return_pref_is_set = !#[allow(non_exhaustive_omitted_patterns)] match return_pref {
    crate::miniextendr_fn::ReturnPref::Auto => true,
    _ => false,
}matches!(return_pref, crate::miniextendr_fn::ReturnPref::Auto);
1486    let fn_return_handling = if unwrap_in_r && crate::return_type_analysis::output_is_result(output)
1487    {
1488        // In unwrap_in_r mode the IntoR here operates on the whole Result<T,E> (the
1489        // framework's IntoR for Result encodes it as a tagged list for R to decode), NOT on
1490        // the inner T. prefer= always targets that inner T, so there is nothing for it to
1491        // wrap here — reject rather than silently drop it (see apply_return_pref docstring
1492        // and the BUG4 audit finding).
1493        if return_pref_is_set {
1494            let span = return_pref_span.unwrap_or_else(proc_macro2::Span::call_site);
1495            return syn::Error::new(
1496                span,
1497                "`prefer = ...` cannot be combined with `unwrap_in_r` on a function returning \
1498                 `Result<T, E>`: `unwrap_in_r` converts the whole `Result<T, E>` via a single \
1499                 `IntoR` impl (a tagged list for R to decode), not the inner `T` alone, so \
1500                 there is no plain `T` for `prefer=` to wrap. Remove `prefer=`.",
1501            )
1502            .into_compile_error()
1503            .into();
1504        }
1505        c_wrapper_builder::ReturnHandling::IntoR
1506    } else {
1507        let auto = c_wrapper_builder::detect_return_handling(output, return_wrap.as_ref());
1508        // Apply return_pref override: wraps the result in AsList/AsExternalPtr/AsRNative.
1509        // Only applies to the plain IntoR variant — Option*/Result*/Unit/RawSexp/ExternalPtr
1510        // variants have no bare T to wrap and now hard-error instead of silently ignoring
1511        // prefer= (see apply_return_pref docstring).
1512        match apply_return_pref(auto, return_pref, return_pref_span) {
1513            Ok(handling) => handling,
1514            Err(err) => return err.into_compile_error().into(),
1515        }
1516    };
1517
1518    // `rng(seed = <param>)` (#2198): the R formal the seed contract reads,
1519    // once the parameter and the return are known to suit it.
1520    let rng_seed_formal = match &rng_seed {
1521        Some(seed) => {
1522            use c_wrapper_builder::ReturnHandling;
1523            let unit_return = #[allow(non_exhaustive_omitted_patterns)] match fn_return_handling {
    ReturnHandling::Unit | ReturnHandling::ResultUnit |
        ReturnHandling::OptionUnit => true,
    _ => false,
}matches!(
1524                fn_return_handling,
1525                ReturnHandling::Unit | ReturnHandling::ResultUnit | ReturnHandling::OptionUnit
1526            );
1527            match miniextendr_fn::check_rng_seed(
1528                seed,
1529                all_inputs,
1530                inputs,
1531                &formals,
1532                &unevaluated_r_names,
1533                unit_return,
1534            ) {
1535                Ok(formal) => Some(formal),
1536                Err(err) => return err.into_compile_error().into(),
1537            }
1538        }
1539        None => None,
1540    };
1541
1542    let thread_strategy = if use_main_thread {
1543        c_wrapper_builder::ThreadStrategy::MainThread
1544    } else {
1545        c_wrapper_builder::ThreadStrategy::WorkerThread
1546    };
1547
1548    // Build CWrapperContext for the standalone fn
1549    let mut c_wrapper_builder =
1550        c_wrapper_builder::CWrapperContext::builder(rust_ident.clone(), c_ident.clone())
1551            .r_name(r_wrapper_ident_str.clone())
1552            .r_wrapper_const(r_wrapper_generator.clone())
1553            .inputs(inputs.iter().cloned().collect())
1554            .output(output.clone())
1555            .call_expr(fn_call_expr)
1556            .thread_strategy(thread_strategy)
1557            .return_handling(fn_return_handling)
1558            .err_parts(err_parts)
1559            .cfg_attrs(cfg_attrs.clone())
1560            .vis(vis.clone())
1561            .generics(generics.clone())
1562            .formals(formals.clone())
1563            .preserve_param_names();
1564
1565    if uses_internal_c_wrapper {
1566        // Normal Rust fn: generate full C wrapper
1567    } else {
1568        // extern "C-unwind" fn: user wrote the C symbol; only emit R_CallMethodDef
1569        c_wrapper_builder = c_wrapper_builder.skip_wrapper();
1570    }
1571
1572    if coerce_all {
1573        c_wrapper_builder = c_wrapper_builder.coerce_all();
1574    }
1575    for param in &coerce_params_list {
1576        c_wrapper_builder = c_wrapper_builder.with_coerce_param(param.clone());
1577    }
1578    for param in match_arg_several_ok_params_list {
1579        c_wrapper_builder = c_wrapper_builder.match_arg_several_ok(param);
1580    }
1581    for (param, leaf) in layered_choice_params_list {
1582        c_wrapper_builder = c_wrapper_builder.layered_choice(param, leaf);
1583    }
1584    for (param, message) in no_na_params_list {
1585        c_wrapper_builder = c_wrapper_builder.no_na(param, message);
1586    }
1587    for (param, markers) in parsed.param_markers() {
1588        c_wrapper_builder = c_wrapper_builder.param_markers(param.clone(), markers.clone());
1589    }
1590    if check_interrupt {
1591        c_wrapper_builder = c_wrapper_builder.check_interrupt();
1592    }
1593    if rng {
1594        c_wrapper_builder = c_wrapper_builder.rng();
1595    }
1596    if strict {
1597        c_wrapper_builder = c_wrapper_builder.strict();
1598    }
1599
1600    let c_wrapper = c_wrapper_builder.build().generate();
1601
1602    // region: R wrappers generation in `fn`
1603    // Build R formal parameters and call arguments using shared builder
1604    let mut arg_builder = RArgumentBuilder::new(inputs).with_formals(formals.clone());
1605    // Add user-specified parameter defaults, keyed by R formal like the
1606    // builder's. A `Missing<T>` default only writes the formal (#2161): the
1607    // call argument tests `missing()` first.
1608    let mut merged_defaults = formals.rekey(parsed.param_defaults());
1609    // For match_arg params, always use the choices placeholder as the formal
1610    // default — the write-time pass replaces it with `c("a", "b", ...)`.
1611    // If the user supplied `default = "\"X\""`, capture X as `preferred_default`
1612    // so the write-time pass rotates X to position 1; the user's literal does
1613    // NOT become the formal (otherwise R's `match.arg` would only see one
1614    // choice).
1615    //
1616    // Tuple: (placeholder, aliases_placeholder, rust_param,
1617    // preferred_default_unquoted_or_empty)
1618    let mut match_arg_placeholders: Vec<(String, String, String, String)> = Vec::new();
1619    for match_arg_param in parsed.match_arg_params() {
1620        let r_name = formals.of(match_arg_param);
1621        let preferred = match merged_defaults.get(&r_name) {
1622            Some(raw) => crate::match_arg_keys::extract_match_arg_default(raw),
1623            None => String::new(),
1624        };
1625        // The placeholders are keyed by the Rust name, never the formal
1626        // (`match_arg_keys::param_key`).
1627        let key = crate::match_arg_keys::param_key(match_arg_param);
1628        let placeholder = crate::match_arg_keys::choices_placeholder(&c_ident.to_string(), &key);
1629        // `Option<T>` (#1473): the formal is `NULL` (no choice); `no_default`
1630        // (#1828, which `default` cannot join): no formal default. The prelude
1631        // spells the choices out through the same placeholder either way, so
1632        // its entry is emitted below in every case.
1633        let formal = match parsed.param_attrs(match_arg_param) {
1634            Some(attrs) => attrs.choice_formal(&placeholder),
1635            None => Some(placeholder.clone()),
1636        };
1637        if let Some(formal) = formal {
1638            merged_defaults.insert(r_name.clone(), formal);
1639        }
1640        let aliases_placeholder =
1641            crate::match_arg_keys::aliases_placeholder(&c_ident.to_string(), &key);
1642        match_arg_placeholders.push((
1643            placeholder,
1644            aliases_placeholder,
1645            match_arg_param.clone(),
1646            preferred,
1647        ));
1648    }
1649    // Add c("a", "b", "c") default for choices params (idiomatic R match.arg
1650    // pattern); an `Option<T>` choices param defaults to `NULL` instead
1651    // (#1473), and a `no_default` one gets none (#1828).
1652    for (param_name, choices) in parsed.choices_params() {
1653        let r_name = formals.of(param_name);
1654        let quoted: Vec<String> = choices.iter().map(|c| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\"{0}\"", c))
    })format!("\"{}\"", c)).collect();
1655        let choices_expr = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("c({0})", quoted.join(", ")))
    })format!("c({})", quoted.join(", "));
1656        let formal = match parsed.param_attrs(param_name) {
1657            Some(attrs) => attrs.choice_formal(&choices_expr),
1658            None => Some(choices_expr),
1659        };
1660        if let Some(formal) = formal {
1661            merged_defaults.entry(r_name).or_insert(formal);
1662        }
1663    }
1664    arg_builder = arg_builder
1665        .with_defaults(merged_defaults)
1666        .with_absent_formals(parsed.absent_formals(&c_ident.to_string()));
1667
1668    let r_formals = arg_builder.build_formals();
1669    let mut r_call_args_strs = arg_builder.build_call_args_vec();
1670
1671    // Prepend the `.call` parameter if using the internal C wrapper. Marker
1672    // (`Call` / `CallerCall`) > attribute (`call = wrapper | caller | none`)
1673    // > crate default (`call_attribution = "..."`, where `caller` applies to
1674    // `noexport` / `internal` entry points only) > `wrapper` (#1566). Every
1675    // form passes the call as written: `sys.call()` for `wrapper`, the
1676    // caller's `sys.call()` for `caller`; `none` passes `FALSE`, the "no
1677    // call" marker, so every condition has a NULL call (#1851). `caller`
1678    // also gives the wrapper a trailing `.call = NULL` formal (#1613, S3
1679    // methods excluded; see `call_formal` below), as does `call_arg`.
1680    // Attribution is independent of the preconditions.
1681    //
1682    // `call_arg` (#1834) turns `wrapper` into `Argument`: the same trailing
1683    // formal, whose `NULL` is the wrapper's own call. It is an explicit
1684    // choice, so the crate default doesn't apply with it (the parser and the
1685    // marker loop above refuse it with `caller` and `none`).
1686    //
1687    // An S3 method's `wrapper` attribution is `Generic` (#1851): the slot
1688    // takes the method's frame, `environment()`, and the raise helper turns
1689    // it into the generic's call (`summary(x)` for `summary.cls(x)`, the
1690    // assignment for a replacement method) only when a condition is raised.
1691    // With a `Call` parameter the frame is resolved up front instead
1692    // (`GenericEager`), since the body receives the slot.
1693    let call_attribution = r_wrapper_builder::CallAttribution::resolve(
1694        call_marker.as_ref().map(|(_, kind)| *kind),
1695        call_attribution_attr,
1696        crate_config.call_attribution.filter(|_| !call_arg),
1697        noexport || internal,
1698    )
1699    .with_call_arg(call_arg);
1700    let call_attribution = if is_s3_method {
1701        call_attribution.for_s3_method(call_marker.is_some())
1702    } else {
1703        call_attribution
1704    };
1705    if uses_internal_c_wrapper {
1706        r_call_args_strs.insert(0, call_attribution.dot_call_arg().to_string());
1707    } else if #[allow(non_exhaustive_omitted_patterns)] match call_attribution_attr {
    Some(r_wrapper_builder::CallAttribution::Caller |
        r_wrapper_builder::CallAttribution::NoCall) => true,
    _ => false,
}matches!(
1708        call_attribution_attr,
1709        Some(
1710            r_wrapper_builder::CallAttribution::Caller | r_wrapper_builder::CallAttribution::NoCall
1711        )
1712    ) || call_arg
1713    {
1714        // `extern "C-unwind"` fns have no generated call slot to redirect (a
1715        // `CallerCall` parameter already fails the extern signature check).
1716        let option = if call_arg {
1717            "`call_arg`"
1718        } else if call_attribution_attr == Some(r_wrapper_builder::CallAttribution::NoCall) {
1719            "`call = none`"
1720        } else {
1721            "`call = caller`"
1722        };
1723        return syn::Error::new_spanned(
1724            &parsed.item().sig.ident,
1725            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} needs the generated call slot; an `extern \"C-unwind\"` function has no `.call` argument to attribute",
                option))
    })format!(
1726                "{option} needs the generated call slot; an `extern \"C-unwind\"` function \
1727                 has no `.call` argument to attribute"
1728            ),
1729        )
1730        .into_compile_error()
1731        .into();
1732    }
1733
1734    // Build the R body string consistently
1735    let c_ident_str = c_ident.to_string();
1736    let call_args_joined = r_call_args_strs.join(", ");
1737    let call_expr = if r_call_args_strs.is_empty() {
1738        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(".Call({0})", c_ident_str))
    })format!(".Call({})", c_ident_str)
1739    } else {
1740        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(".Call({0}, {1})", c_ident_str,
                call_args_joined))
    })format!(".Call({}, {})", c_ident_str, call_args_joined)
1741    };
1742    let r_wrapper_return_str = {
1743        // Capture result, check for tagged condition value, raise R condition if present.
1744        let final_return = return_wrap
1745            .as_ref()
1746            .map_or_else(|| ".val".to_owned(), |wrap| wrap.r_expression(".val", None));
1747        let final_return = if is_invisible_return_type {
1748            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("invisible({0})", final_return))
    })format!("invisible({final_return})")
1749        } else {
1750            final_return
1751        };
1752        // `rng(seed = ..)` (#2198): the result records the seed, after its
1753        // condition check and before any wrap.
1754        let final_return = if rng_seed_formal.is_some() {
1755            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}\n  {1}",
                r_wrapper_builder::RngSeedContract::ATTRIBUTE, final_return))
    })format!(
1756                "{}\n  {final_return}",
1757                r_wrapper_builder::RngSeedContract::ATTRIBUTE
1758            )
1759        } else {
1760            final_return
1761        };
1762        // `call = caller` binds `.mx_call` at the head of the prelude (see
1763        // `combined_prelude` below), ahead of every R-side check.
1764        crate::method_return_builder::standalone_body_with_call_default(
1765            &call_expr,
1766            &final_return,
1767            "  ",
1768            call_attribution.raise_default(),
1769        )
1770    };
1771    // The trailing `.call = NULL` formal of a `caller` wrapper (#1613) or a
1772    // `call_arg` one (#1834): a hand-written helper between the public
1773    // function and the entry point, or an R function composing the function,
1774    // passes a frame there. S3 methods keep the zero-argument prelude: a
1775    // formal the generic lacks breaks generic/method consistency (`call_arg`
1776    // is refused on them).
1777    // `.call` joins the formals string only, never `inputs`, so the
1778    // precondition builder, the marker peels and the parameter fillers never
1779    // see it as a Rust parameter.
1780    let call_formal = call_attribution.formal().filter(|_| !is_s3_method);
1781    // Stable, consistent R formatting style: brace on same line, body indented, closing brace on its own line
1782    // r_formals is already a joined string from build_formals()
1783    let formals_joined = r_wrapper_builder::with_call_formal(&r_formals, call_formal);
1784    let mut roxygen_tags = if let Some(ref doc_text) = doc {
1785        // Custom doc override: each line becomes a separate roxygen tag entry
1786        doc_text.lines().map(|l| l.to_string()).collect()
1787    } else {
1788        // `noexport` renders no page (it appends `@noRd` below), so its docs
1789        // keep no links (#1818); the reader sees an explicit `@noRd` itself.
1790        crate::roxygen::roxygen_tags_from_attrs(
1791            attrs,
1792            crate::roxygen::RdPage::suppressed_if(noexport),
1793        )
1794    };
1795
1796    // Determine lifecycle: explicit attr > #[deprecated] extraction
1797    let lifecycle_spec = lifecycle.or_else(|| {
1798        attrs
1799            .iter()
1800            .find_map(crate::lifecycle::parse_rust_deprecated)
1801    });
1802
1803    // Inject lifecycle badge into roxygen tags if present
1804    if let Some(ref spec) = lifecycle_spec {
1805        crate::lifecycle::inject_lifecycle_badge(&mut roxygen_tags, spec);
1806    }
1807
1808    // Generated @param tags for the parameters the author left undocumented,
1809    // unless the block takes them from a `@describeIn` topic or
1810    // `@inheritParams` (#1590); the wrapper registry decides the rest per page
1811    // at write time. The match_arg placeholders feed the
1812    // MX_MATCH_ARG_PARAM_DOCS entries of the write-time resolver. The `.call`
1813    // formal gets its line after the parameters' (#1613, #1834), unless the
1814    // block renders no page (`noexport`, `@noRd`): it is no filler, so the
1815    // registry would not drop it there.
1816    let renders_no_page = noexport || crate::roxygen::has_roxygen_tag(&roxygen_tags, "noRd");
1817    let call_param_doc = call_formal
1818        .and(call_attribution.param_doc())
1819        .filter(|_| !renders_no_page);
1820    let match_arg_param_doc_placeholders = crate::roxygen::push_fn_param_tags(
1821        &mut roxygen_tags,
1822        inputs,
1823        &parsed,
1824        &c_ident_str,
1825        call_param_doc,
1826    );
1827
1828    // A standalone function's reference page is titled by its R wrapper name — never
1829    // the doc-comment prose. rustdoc summaries are markdown (intra-doc links, code
1830    // spans) that roxygen2 can't resolve as a `\title`; the prose is promoted to
1831    // `@description` by `roxygen_tags_from_attrs` instead. Without a `@title`, roxygen2
1832    // skips the `.Rd` entirely (#1054), so inject the wrapper name when none exists.
1833    // A block that renders no page (`@noRd`, or `noexport` which appends it below)
1834    // needs no title (#1552).
1835    let has_no_rd_tag = crate::roxygen::has_roxygen_tag(&roxygen_tags, "noRd");
1836    if !roxygen_tags.is_empty()
1837        && !has_no_rd_tag
1838        && !noexport
1839        && !crate::roxygen::has_roxygen_tag(&roxygen_tags, "title")
1840    {
1841        roxygen_tags.insert(0, ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("@title {0}", r_wrapper_ident_str))
    })format!("@title {}", r_wrapper_ident_str));
1842    }
1843
1844    let roxygen_tags_str = crate::roxygen::format_roxygen_tags(&roxygen_tags);
1845    let has_export_tag = crate::roxygen::has_export_tag(&roxygen_tags);
1846    let has_internal_tag = crate::roxygen::has_roxygen_tag(&roxygen_tags, "keywords internal");
1847    // `@source` provenance is opt-in per crate (#1552); the `# Generated from
1848    // Rust fn … (file:line:col)` comment below stays the navigation pointer.
1849    let source_comment = crate::roxygen::source_tag_with(
1850        crate_config.source_tags,
1851        &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Generated by miniextendr from Rust fn `{0}`",
                rust_ident))
    })format!("Generated by miniextendr from Rust fn `{rust_ident}`"),
1852    )
1853    .map(|line| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}\n", line))
    })format!("{line}\n"))
1854    .unwrap_or_default();
1855    // Inject @keywords internal if #[miniextendr(internal)] and not already present
1856    let internal_comment = if internal && !has_internal_tag {
1857        "#' @keywords internal\n"
1858    } else {
1859        ""
1860    };
1861    // S3 methods need both @method (for registration) AND @export (for NAMESPACE)
1862    // Don't auto-export functions marked with @noRd, @keywords internal, or attr flags
1863    // #[miniextendr(export)] forces @export even on non-pub functions
1864    let export_comment = if (#[allow(non_exhaustive_omitted_patterns)] match vis {
    syn::Visibility::Public(_) => true,
    _ => false,
}matches!(vis, syn::Visibility::Public(_)) || export)
1865        && !has_export_tag
1866        && !has_no_rd_tag
1867        && !has_internal_tag
1868        && !internal
1869        && !noexport
1870    {
1871        // A method on `pkg::generic` registers through `@exportS3Method
1872        // pkg::generic class`: roxygen2 writes `S3method(pkg::generic, class)`,
1873        // which R applies once `pkg`'s namespace loads, so `pkg` may stay in
1874        // Suggests (#2154).
1875        s3_qualified_registration.unwrap_or_else(|| "#' @export\n".to_string())
1876    } else {
1877        String::new()
1878    };
1879    // `noexport` means no man page at all (docs/CLASS_SYSTEMS.md export-control
1880    // table): inject @noRd so roxygen2 skips the Rd and the write-time page
1881    // pass (registry.rs) gives the fn no default title. Without this, an
1882    // unexported fn keeps a \usage entry in man/, which R CMD check flags as a
1883    // code/documentation mismatch.
1884    let no_rd_comment = if noexport && !has_no_rd_tag {
1885        "#' @noRd\n"
1886    } else {
1887        ""
1888    };
1889    // Generate match.arg prelude for parameters with #[miniextendr(match_arg)]
1890    // Collect (r_param_name, rust_name, rust_type) for each match_arg param
1891    let match_arg_param_info: Vec<(String, String, &syn::Type)> = inputs
1892        .iter()
1893        .filter_map(|arg| {
1894            if let syn::FnArg::Typed(pt) = arg
1895                && let syn::Pat::Ident(pat_ident) = pt.pat.as_ref()
1896            {
1897                let rust_name = crate::naming::ident_name(&pat_ident.ident);
1898                if parsed.has_match_arg_attr(&rust_name) {
1899                    let r_name = formals.of(&rust_name);
1900                    return Some((r_name, rust_name, pt.ty.as_ref()));
1901                }
1902            }
1903            None
1904        })
1905        .collect();
1906
1907    let match_arg_prelude = if match_arg_param_info.is_empty() {
1908        String::new()
1909    } else {
1910        let mut lines = Vec::new();
1911        for (r_param, rust_name, _) in &match_arg_param_info {
1912            // Every form names the choice list through the same placeholder the
1913            // formal default carries (populated by the write-time pass as
1914            // `c("a", "b", ...)`; it substitutes every occurrence). Strict
1915            // several_ok (#1472): every element must match; NULL selects every
1916            // choice. `Option<T>` (#1473): NULL means no choice and skips the
1917            // check; `Missing<T>` (#1551) skips it for an omitted argument and,
1918            // for a single choice, for the formal passed whole (#2152). The
1919            // helpers read a factor as its labels (#1552). Under `call = caller`
1920            // every form raises with `.mx_call` (#1548).
1921            let key = crate::match_arg_keys::param_key(rust_name);
1922            let placeholder =
1923                crate::match_arg_keys::choices_placeholder(&c_ident.to_string(), &key);
1924            let aliases = crate::match_arg_keys::aliases_placeholder(&c_ident.to_string(), &key);
1925            if let Some(attrs) = parsed.param_attrs(rust_name) {
1926                lines.push(call_attribution.match_arg_statement(
1927                    r_param,
1928                    &placeholder,
1929                    Some(&aliases),
1930                    attrs,
1931                ));
1932            }
1933        }
1934        lines.join("\n  ")
1935    };
1936
1937    // Generate the match.arg prelude for choices params: the same three forms
1938    // as `match_arg`, with the literal choice list known here.
1939    let choices_prelude = {
1940        let mut lines = Vec::new();
1941        for arg in inputs.iter() {
1942            if let syn::FnArg::Typed(pt) = arg
1943                && let syn::Pat::Ident(pat_ident) = pt.pat.as_ref()
1944            {
1945                let rust_name = crate::naming::ident_name(&pat_ident.ident);
1946                if let Some(attrs) = parsed.param_attrs(&rust_name)
1947                    && let Some(choices) = attrs.choices.as_ref()
1948                {
1949                    let r_name = formals.of(&rust_name);
1950                    let quoted: Vec<String> =
1951                        choices.iter().map(|c| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\"{0}\"", c))
    })format!("\"{}\"", c)).collect();
1952                    let choices_expr = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("c({0})", quoted.join(", ")))
    })format!("c({})", quoted.join(", "));
1953                    lines.push(call_attribution.match_arg_statement(
1954                        &r_name,
1955                        &choices_expr,
1956                        None,
1957                        attrs,
1958                    ));
1959                }
1960            }
1961        }
1962        if lines.is_empty() {
1963            String::new()
1964        } else {
1965            lines.join("\n  ")
1966        }
1967    };
1968
1969    // Generate lifecycle prelude if needed
1970    let lifecycle_prelude = lifecycle_spec
1971        .as_ref()
1972        .and_then(|spec| spec.r_prelude(&r_wrapper_ident_str));
1973
1974    // Generate R-side precondition checks (one `isTRUE()` guard per check)
1975    // Skip both match_arg and choices params (already validated by match.arg)
1976    // and `Quoted` / `Quosure` params, which no check may force (#1835).
1977    let mut skip_params = parsed.precondition_skip_params();
1978    skip_params.extend(unevaluated_r_names.iter().cloned());
1979    // A parameter drops its type-derived checks when its own spelling
1980    // (`Unchecked<T>`, `#[miniextendr(no_preconditions)]` on it), else the
1981    // function's `no_preconditions`, else the crate's `preconditions = false`,
1982    // else the `no-preconditions-default` feature says so (#1566,
1983    // `r_preconditions::resolve_type_checks`). TryFromSexp still raises a
1984    // typed Rust error on mismatched input. The savings were measured against
1985    // the former `stopifnot()` block (~1230 ns / 1-arg or ~3900 ns / 5-arg);
1986    // the guards cost about half. The per-parameter `inherits` /
1987    // `not_inherits` / `no_na` checks stay: the Rust conversion does not
1988    // repeat them. (The C wrapper's
1989    // post-conversion `no_na` check covers only what a type reads as `NA`
1990    // beyond `anyNA()`, such as `"NA"` for `AsNumeric`, except on an
1991    // `Either`, which has no R guard and is checked only in Rust.)
1992    let precondition_prelude = {
1993        // `set.seed()` validates the seed of `rng(seed = ..)` (#2198), so
1994        // its parameter has no type-derived check.
1995        let mut unchecked = r_preconditions::unchecked_params(
1996            inputs,
1997            &formals,
1998            |name| parsed.param_attrs(name).and_then(|a| a.preconditions),
1999            preconditions,
2000            None,
2001            crate_config.preconditions,
2002        );
2003        unchecked.extend(rng_seed_formal.iter().cloned());
2004        // A coerced integer-element vector reads via `&[i32]` (INTSXP-only), so its
2005        // precondition tightens to `is.integer` (issue #616). `coerce_params_list`
2006        // holds Rust names; the options are keyed by R formal.
2007        let precondition_opts = r_preconditions::PreconditionOptions {
2008            coerce_all,
2009            coerce_params: coerce_params_list.iter().map(|p| formals.of(p)).collect(),
2010            explicit: parsed.explicit_checks(),
2011            unchecked,
2012            formals: formals.clone(),
2013        };
2014        let precondition_output =
2015            r_preconditions::build_precondition_checks(inputs, &skip_params, &precondition_opts);
2016        // One guard per check, raising the shared argument error (#1591):
2017        // attributed to the wrapper's own call, or under `call = caller` to
2018        // `.mx_call` (#1548).
2019        precondition_output
2020            .guards(call_attribution.r_check_call())
2021            .join("\n  ")
2022    };
2023
2024    // `dots = empty | forward(..)` (#2157): refuse what `...` doesn't take,
2025    // ahead of the argument checks, so a misspelt argument is reported before
2026    // the missing one it was meant to be.
2027    let dots_check = dots_policy
2028        .as_ref()
2029        .and_then(|policy| policy.r_check(&formals_joined, call_attribution.r_check_call()));
2030
2031    // Combine all preludes: call attribution (`.mx_call` under `call = caller`,
2032    // first so the R-side checks below can raise with it, #1548), r_entry,
2033    // on.exit, lifecycle, the `...` check, static preconditions, match.arg,
2034    // choices, r_post_checks, and last the seed contract of `rng(seed = ..)`
2035    // (#2198), so that a refused argument changes no RNG state. (Missing<T>
2036    // forwarding lives inline in the `.Call()` args — see
2037    // `build_call_args_vec` — because a prelude binding of the missing
2038    // sentinel errors on lookup.)
2039    let on_exit_str = r_on_exit.as_ref().map(|oe| oe.to_r_code());
2040    let rng_seed_prelude = rng_seed_formal
2041        .as_deref()
2042        .map(|formal| r_wrapper_builder::RngSeedContract { formal }.prelude("  "));
2043    let attribution_prelude = call_attribution.prelude(call_formal.is_some());
2044    let combined_prelude = {
2045        let mut parts = Vec::new();
2046        if !attribution_prelude.is_empty() {
2047            parts.push(attribution_prelude.as_str());
2048        }
2049        if let Some(ref entry) = r_entry {
2050            parts.push(entry.as_str());
2051        }
2052        if let Some(ref s) = on_exit_str {
2053            parts.push(s.as_str());
2054        }
2055        if let Some(ref lc) = lifecycle_prelude {
2056            parts.push(lc.as_str());
2057        }
2058        if let Some(ref check) = dots_check {
2059            parts.push(check.as_str());
2060        }
2061        if !precondition_prelude.is_empty() {
2062            parts.push(&precondition_prelude);
2063        }
2064        if !match_arg_prelude.is_empty() {
2065            parts.push(&match_arg_prelude);
2066        }
2067        if !choices_prelude.is_empty() {
2068            parts.push(&choices_prelude);
2069        }
2070        if let Some(ref post) = r_post_checks {
2071            parts.push(post.as_str());
2072        }
2073        if let Some(ref seed) = rng_seed_prelude {
2074            parts.push(seed.as_str());
2075        }
2076        if parts.is_empty() {
2077            None
2078        } else {
2079            Some(parts.join("\n  "))
2080        }
2081    };
2082
2083    let r_wrapper_string = if let Some(prelude) = combined_prelude {
2084        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}{1}{2}{3}{4}{5}{6} <- function({7}) {{\n  {8}\n  {9}\n}}",
                roxygen_tags_str, source_comment, s3_method_comment,
                internal_comment, no_rd_comment, export_comment,
                crate::naming::r_def_name(&r_wrapper_ident_str),
                formals_joined, prelude, r_wrapper_return_str))
    })format!(
2085            "{}{}{}{}{}{}{} <- function({}) {{\n  {}\n  {}\n}}",
2086            roxygen_tags_str,
2087            source_comment,
2088            s3_method_comment,
2089            internal_comment,
2090            no_rd_comment,
2091            export_comment,
2092            crate::naming::r_def_name(&r_wrapper_ident_str),
2093            formals_joined,
2094            prelude,
2095            r_wrapper_return_str
2096        )
2097    } else {
2098        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}{1}{2}{3}{4}{5}{6} <- function({7}) {{\n  {8}\n}}",
                roxygen_tags_str, source_comment, s3_method_comment,
                internal_comment, no_rd_comment, export_comment,
                crate::naming::r_def_name(&r_wrapper_ident_str),
                formals_joined, r_wrapper_return_str))
    })format!(
2099            "{}{}{}{}{}{}{} <- function({}) {{\n  {}\n}}",
2100            roxygen_tags_str,
2101            source_comment,
2102            s3_method_comment,
2103            internal_comment,
2104            no_rd_comment,
2105            export_comment,
2106            crate::naming::r_def_name(&r_wrapper_ident_str),
2107            formals_joined,
2108            r_wrapper_return_str
2109        )
2110    };
2111    // Use a raw string literal for better readability in macro expansion
2112    let r_wrapper_str = r_wrapper_raw_literal(
2113        &r_wrapper_string,
2114        &r_shadowing::formal_names([(inputs, formals.clone())]),
2115    );
2116
2117    // endregion
2118
2119    // Generate doc strings with links
2120    let r_wrapper_doc = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("R wrapper code for [`{0}`], calls [`{1}`].",
                rust_ident, c_ident))
    })format!(
2121        "R wrapper code for [`{}`], calls [`{}`].",
2122        rust_ident, c_ident
2123    );
2124    let source_start = rust_ident.span().start();
2125    let source_line_lit = syn::LitInt::new(&source_start.line.to_string(), rust_ident.span());
2126    let source_col_lit =
2127        syn::LitInt::new(&(source_start.column + 1).to_string(), rust_ident.span());
2128
2129    // Get the normalized item for output, with roxygen tags stripped from docs.
2130    // Roxygen tags are for R documentation and shouldn't appear in rustdoc.
2131    let mut original_item = parsed.item_without_roxygen();
2132    // Strip only the miniextendr attributes; keep everything else.
2133    original_item
2134        .attrs
2135        .retain(|attr| !attr.path().is_ident("miniextendr"));
2136
2137    // Inject dots_typed binding into function body if dots = typed_list!(...) was specified
2138    // (the gate above guarantees the dots parameter exists).
2139    if let (Some(spec_tokens), Some(dots_param)) = (&dots_spec, &dots_ident) {
2140        let validation_stmt = build_dots_validation_stmt(dots_param, spec_tokens);
2141        original_item.block.stmts.insert(0, validation_stmt);
2142    }
2143
2144    let original_item = original_item;
2145
2146    // Generate MX_MATCH_ARG_CHOICES entries for placeholder → choices replacement
2147    // Resolve the `MatchArg`-bound type used in the choices_str closure: for
2148    // `several_ok` params that's the inner element of the container, otherwise
2149    // it's the param type directly.
2150    let choices_ty_for = |rust_param: &str| -> Option<&syn::Type> {
2151        let (_, _, param_ty) = match_arg_param_info
2152            .iter()
2153            .find(|(_, rn, _)| rn == rust_param)?;
2154        Some(match_arg_choices_ty(
2155            param_ty,
2156            parsed.has_several_ok(rust_param),
2157        ))
2158    };
2159
2160    let match_arg_choices_entries: Vec<proc_macro2::TokenStream> = match_arg_placeholders
2161        .iter()
2162        .filter_map(
2163            |(placeholder, aliases_placeholder, rust_param, preferred_default)| {
2164                let choices_ty = choices_ty_for(rust_param)?;
2165                let entry_ident = syn::Ident::new(
2166                    &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("match_arg_choices_entry_{0}",
                crate::match_arg_keys::placeholder_ident_suffix(placeholder)))
    })format!(
2167                        "match_arg_choices_entry_{}",
2168                        crate::match_arg_keys::placeholder_ident_suffix(placeholder)
2169                    ),
2170                    proc_macro2::Span::call_site(),
2171                );
2172                Some(crate::match_arg_keys::choices_entry_tokens(
2173                    &cfg_attrs,
2174                    &entry_ident,
2175                    placeholder,
2176                    aliases_placeholder,
2177                    choices_ty,
2178                    preferred_default,
2179                ))
2180            },
2181        )
2182        .collect();
2183
2184    // Generate MX_MATCH_ARG_PARAM_DOCS entries for @param doc placeholder → choice description
2185    let match_arg_param_doc_entries: Vec<proc_macro2::TokenStream> =
2186        match_arg_param_doc_placeholders
2187            .iter()
2188            .filter_map(|(doc_placeholder, rust_param)| {
2189                let choices_ty = choices_ty_for(rust_param)?;
2190                let attrs = parsed.param_attrs(rust_param)?;
2191                let entry_ident = syn::Ident::new(
2192                    &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("match_arg_param_doc_entry_{0}",
                crate::match_arg_keys::placeholder_ident_suffix(doc_placeholder)))
    })format!(
2193                        "match_arg_param_doc_entry_{}",
2194                        crate::match_arg_keys::placeholder_ident_suffix(doc_placeholder)
2195                    ),
2196                    proc_macro2::Span::call_site(),
2197                );
2198                Some(crate::match_arg_keys::param_doc_entry_tokens(
2199                    &cfg_attrs,
2200                    &entry_ident,
2201                    doc_placeholder,
2202                    attrs.several_ok,
2203                    &attrs.choice_doc_suffix(),
2204                    choices_ty,
2205                ))
2206            })
2207            .collect();
2208
2209    // Generate doc comment linking to C wrapper and R wrapper constant
2210    let fn_r_wrapper_doc = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("See [`{0}`] for C wrapper, [`{1}`] for R wrapper.",
                c_ident, r_wrapper_generator))
    })format!(
2211        "See [`{}`] for C wrapper, [`{}`] for R wrapper.",
2212        c_ident, r_wrapper_generator
2213    );
2214
2215    let expanded: T = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::ToTokens::to_tokens(&fn_r_wrapper_doc, &mut _s);
            _s
        });
    ::quote::ToTokens::to_tokens(&original_item, &mut _s);
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut cfg_attrs, i) = cfg_attrs.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let cfg_attrs =
                match cfg_attrs.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&cfg_attrs, &mut _s);
        }
    }
    ::quote::ToTokens::to_tokens(&c_wrapper, &mut _s);
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut cfg_attrs, i) = cfg_attrs.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let cfg_attrs =
                match cfg_attrs.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&cfg_attrs, &mut _s);
        }
    }
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::ToTokens::to_tokens(&r_wrapper_doc, &mut _s);
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "concat");
            ::quote::__private::push_bang(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::parse(&mut _s,
                        "\"Wraps Rust function `\"");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "stringify");
                    ::quote::__private::push_bang(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::ToTokens::to_tokens(&rust_ident, &mut _s);
                            _s
                        });
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\"`.\"");
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::ToTokens::to_tokens(&source_loc_doc, &mut _s);
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "concat");
            ::quote::__private::push_bang(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::parse(&mut _s,
                        "\"Generated from source file `\"");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "file");
                    ::quote::__private::push_bang(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\"`.\"");
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "cfg_attr");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "not");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "target_arch");
                            ::quote::__private::push_eq(&mut _s);
                            ::quote::__private::parse(&mut _s, "\"wasm32\"");
                            _s
                        });
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "linkme");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s,
                        "distributed_slice");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "registry");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "MX_R_WRAPPERS");
                            _s
                        });
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "linkme");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "crate");
                            ::quote::__private::push_eq(&mut _s);
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "linkme");
                            _s
                        });
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s,
                        "non_upper_case_globals");
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "non_snake_case");
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "static");
    ::quote::ToTokens::to_tokens(&r_wrapper_generator, &mut _s);
    ::quote::__private::push_colon(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "registry");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "RWrapperEntry");
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "registry");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "RWrapperEntry");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "priority");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "registry");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "RWrapperPriority");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Function");
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_ident(&mut _s, "source_file");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "file");
            ::quote::__private::push_bang(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                ::quote::__private::TokenStream::new());
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_ident(&mut _s, "source_line");
            ::quote::__private::push_colon(&mut _s);
            ::quote::ToTokens::to_tokens(&source_line_lit, &mut _s);
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_ident(&mut _s, "content");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "concat");
            ::quote::__private::push_bang(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::parse(&mut _s,
                        "\"# Generated from Rust fn `\"");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "stringify");
                    ::quote::__private::push_bang(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::ToTokens::to_tokens(&rust_ident, &mut _s);
                            _s
                        });
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\"` (\"");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "file");
                    ::quote::__private::push_bang(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\":\"");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::ToTokens::to_tokens(&source_line_lit, &mut _s);
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\":\"");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::ToTokens::to_tokens(&source_col_lit, &mut _s);
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\")\"");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::ToTokens::to_tokens(&r_wrapper_str, &mut _s);
                    _s
                });
            ::quote::__private::push_comma(&mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut match_arg_choices_entries, i) =
            match_arg_choices_entries.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let match_arg_choices_entries =
                match match_arg_choices_entries.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&match_arg_choices_entries, &mut _s);
        }
    }
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut match_arg_param_doc_entries, i) =
            match_arg_param_doc_entries.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let match_arg_param_doc_entries =
                match match_arg_param_doc_entries.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&match_arg_param_doc_entries,
                &mut _s);
        }
    }
    ::quote::ToTokens::to_tokens(&doc_lint_warnings, &mut _s);
    _s
}quote::quote! {
2216        // rust function with doc link to R wrapper
2217        #[doc = #fn_r_wrapper_doc]
2218        #original_item
2219
2220        // C wrapper
2221        #(#cfg_attrs)*
2222        #c_wrapper
2223
2224        // R wrapper (self-registers via distributed_slice)
2225        #(#cfg_attrs)*
2226        #[doc = #r_wrapper_doc]
2227        #[doc = ::core::concat!("Wraps Rust function `", ::core::stringify!(#rust_ident), "`.")]
2228        #[doc = #source_loc_doc]
2229        #[doc = ::core::concat!("Generated from source file `", ::core::file!(), "`.")]
2230        #[cfg_attr(not(target_arch = "wasm32"), ::miniextendr_api::linkme::distributed_slice(::miniextendr_api::registry::MX_R_WRAPPERS), linkme(crate = ::miniextendr_api::linkme))]
2231        #[allow(non_upper_case_globals)]
2232        #[allow(non_snake_case)]
2233        static #r_wrapper_generator: ::miniextendr_api::registry::RWrapperEntry =
2234            ::miniextendr_api::registry::RWrapperEntry {
2235                priority: ::miniextendr_api::registry::RWrapperPriority::Function,
2236                source_file: ::core::file!(),
2237                source_line: #source_line_lit,
2238                content: ::core::concat!(
2239                    "# Generated from Rust fn `",
2240                    ::core::stringify!(#rust_ident),
2241                    "` (",
2242                    ::core::file!(),
2243                    ":",
2244                    #source_line_lit,
2245                    ":",
2246                    #source_col_lit,
2247                    ")",
2248                    #r_wrapper_str
2249                ),
2250            };
2251
2252        // match_arg choices entries for R wrapper default replacement
2253        #(#match_arg_choices_entries)*
2254
2255        // match_arg @param doc entries for R wrapper roxygen doc replacement
2256        #(#match_arg_param_doc_entries)*
2257
2258        // doc-lint warnings (if any)
2259        #doc_lint_warnings
2260    }
2261    .into();
2262
2263    expanded
2264}
2265
2266/// Maps a `ReturnPref` attribute value onto an auto-detected `ReturnHandling`.
2267///
2268/// Only the plain `IntoR` variant has a bare `T` for `prefer=` to wrap, so it is the
2269/// only variant substituted with its pref-specific counterpart
2270/// (`AsListOf`/`AsExternalPtrOf`/`AsNativeOf`). Every other variant (`Unit`, `RawSexp`,
2271/// `ExternalPtr`, `Option*`, `Result*`) has its own fixed SEXP-shape rule that
2272/// `prefer=` cannot compose with — returning a compile error for those is better than
2273/// silently dropping the attribute (see the BUG4 audit finding: `prefer = "list"` on an
2274/// `Option<T>` return used to be accepted and silently ignored).
2275fn apply_return_pref(
2276    auto: c_wrapper_builder::ReturnHandling,
2277    pref: crate::miniextendr_fn::ReturnPref,
2278    pref_span: Option<proc_macro2::Span>,
2279) -> syn::Result<c_wrapper_builder::ReturnHandling> {
2280    use crate::miniextendr_fn::ReturnPref;
2281    use c_wrapper_builder::ReturnHandling;
2282
2283    let wrapped = match pref {
2284        ReturnPref::Auto => return Ok(auto),
2285        ReturnPref::List => match auto {
2286            ReturnHandling::IntoR => Some(ReturnHandling::AsListOf),
2287            _ => None,
2288        },
2289        ReturnPref::ExternalPtr => match auto {
2290            ReturnHandling::IntoR => Some(ReturnHandling::AsExternalPtrOf),
2291            _ => None,
2292        },
2293        ReturnPref::Native => match auto {
2294            ReturnHandling::IntoR => Some(ReturnHandling::AsNativeOf),
2295            _ => None,
2296        },
2297    };
2298
2299    wrapped.ok_or_else(|| {
2300        let (pref_name, wrapper_name) = return_pref_names(pref);
2301        let span = pref_span.unwrap_or_else(proc_macro2::Span::call_site);
2302        syn::Error::new(
2303            span,
2304            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`prefer = \"{1}\"` cannot be honored on this return type. `prefer=` only applies to a function returning a plain `T: IntoR` value, which it wraps in `{2}` before conversion. This function\'s return type falls into a different codegen category ({0}) with its own fixed SEXP-shape rule, so there is no plain `T` for `prefer=` to wrap. Remove `prefer=`, or change the return type to a plain `T`.",
                return_handling_category_description(&auto), pref_name,
                wrapper_name))
    })format!(
2305                "`prefer = \"{pref_name}\"` cannot be honored on this return type. \
2306                 `prefer=` only applies to a function returning a plain `T: IntoR` value, \
2307                 which it wraps in `{wrapper_name}` before conversion. This function's return \
2308                 type falls into a different codegen category ({}) with its own fixed \
2309                 SEXP-shape rule, so there is no plain `T` for `prefer=` to wrap. Remove \
2310                 `prefer=`, or change the return type to a plain `T`.",
2311                return_handling_category_description(&auto),
2312            ),
2313        )
2314    })
2315}
2316
2317/// Human-readable `(attribute value, wrapper type)` pair for a [`ReturnPref`](crate::miniextendr_fn::ReturnPref),
2318/// used to phrase the `apply_return_pref` compile error.
2319fn return_pref_names(pref: crate::miniextendr_fn::ReturnPref) -> (&'static str, &'static str) {
2320    use crate::miniextendr_fn::ReturnPref;
2321    match pref {
2322        ReturnPref::Auto => ("auto", ""),
2323        ReturnPref::List => ("list", "AsList"),
2324        ReturnPref::ExternalPtr => ("externalptr", "AsExternalPtr"),
2325        ReturnPref::Native => ("native", "AsRNative"),
2326    }
2327}
2328
2329/// Human-readable description of a [`ReturnHandling`](c_wrapper_builder::ReturnHandling)
2330/// category, for the `apply_return_pref` compile error. Only describes the categories
2331/// [`c_wrapper_builder::detect_return_handling`] can actually produce;
2332/// the wildcard arm covers variants that never reach `apply_return_pref` as `auto`
2333/// (`IntoR` itself, method-only `SelfHandle`, and the `As*Of` variants `apply_return_pref`
2334/// produces as *output*, never takes as input).
2335fn return_handling_category_description(rh: &c_wrapper_builder::ReturnHandling) -> &'static str {
2336    use c_wrapper_builder::ReturnHandling;
2337    match rh {
2338        ReturnHandling::Unit => "the unit return type `()`",
2339        ReturnHandling::RawSexp => "a raw `SEXP` return type",
2340        ReturnHandling::ExternalPtr => {
2341            "a `Self`-returning constructor, already converted via `ExternalPtr::new`"
2342        }
2343        ReturnHandling::OptionUnit => "`Option<()>`",
2344        ReturnHandling::OptionSexp => "`Option<SEXP>`",
2345        ReturnHandling::OptionIntoR | ReturnHandling::OptionClassPayload => "`Option<T>`",
2346        ReturnHandling::ResultUnit => "`Result<(), E>`",
2347        ReturnHandling::ResultSexp => "`Result<SEXP, E>`",
2348        ReturnHandling::ResultIntoR => "`Result<T, E>`",
2349        ReturnHandling::ResultNullOnErr => "`Result<T, ()>`",
2350        _ => "this return type",
2351    }
2352}
2353
2354/// Generate thread-safe wrappers for R FFI functions.
2355///
2356/// Apply this to an `extern "C-unwind"` block to generate, **for each
2357/// non-variadic function**, a pair of entry points:
2358///
2359/// - The original name (e.g. `Rf_allocVector`) — a safe Rust wrapper that
2360///   runs directly on R's main thread, routes through
2361///   `miniextendr_api::worker::with_r_thread` from an active miniextendr
2362///   worker context, and panics for arbitrary off-main callers.
2363/// - A `*_unchecked` sibling (`Rf_allocVector_unchecked`) — the raw
2364///   `extern "C-unwind"` declaration with no main-thread assertion and no
2365///   worker round-trip.
2366///
2367/// User code should reach for the checked variant by default; the unchecked
2368/// sibling exists for three known-safe contexts:
2369///
2370/// 1. **Inside ALTREP callbacks** — R is already calling us on the main
2371///    thread, so the assertion would always pass and the route would
2372///    deadlock the call back to R.
2373/// 2. **Inside a `with_r_unwind_protect` body** — the guard has established
2374///    main-thread context, and re-entering `with_r_thread` would nest two
2375///    `R_UnwindProtect` frames (paying the longjmp-leak cost twice).
2376/// 3. **Inside a `with_r_thread` body** — the assertion is redundant; you
2377///    are already where you needed to be.
2378///
2379/// The build-time lint **MXL301** enforces this: calling `*_unchecked`
2380/// outside one of those three contexts is a compile-time error. Without the
2381/// `worker-thread` feature, the checked variant still enforces the recorded
2382/// main-thread contract; it simply has no worker route available.
2383///
2384/// # Tradeoffs at a glance
2385///
2386/// | Variant | Asserts main thread | Routes to main | When to use |
2387/// |---|---|---|---|
2388/// | `Rf_foo` (checked) | yes (debug) | yes (from worker) | default |
2389/// | `Rf_foo_unchecked` | no | no | ALTREP callbacks, `with_r_unwind_protect`, `with_r_thread` |
2390///
2391/// # Behavior
2392///
2393/// All non-variadic functions are routed to the main thread via `with_r_thread`
2394/// when called from a worker thread. The return value is wrapped in `Sendable`
2395/// and sent back to the caller. This applies to both value-returning functions
2396/// (SEXP, i32, etc.) and pointer-returning functions (`*const T`, `*mut T`).
2397///
2398/// Pointer-returning functions (like `INTEGER`, `REAL`) are safe to route because
2399/// the underlying SEXP must be GC-protected by the caller, and R's GC only runs
2400/// during R API calls which are serialized through `with_r_thread`.
2401///
2402/// # Initialization Requirement
2403///
2404/// `miniextendr_runtime_init()` must be called before using any wrapped function.
2405/// Calling before initialization will panic with a descriptive error message.
2406///
2407/// # Limitations
2408///
2409/// - Variadic functions are passed through unchanged (no wrapper)
2410/// - Statics are passed through unchanged
2411/// - Functions with `#[link_name]` are passed through unchanged
2412///
2413/// # Example
2414///
2415/// ```ignore
2416/// #[r_ffi_checked]
2417/// unsafe extern "C-unwind" {
2418///     // Routed to main thread via with_r_thread when called from worker
2419///     pub fn Rf_ScalarInteger(arg1: i32) -> SEXP;
2420///     pub fn INTEGER(x: SEXP) -> *mut i32;
2421/// }
2422/// ```
2423#[proc_macro_attribute]
2424pub fn r_ffi_checked(
2425    _attr: proc_macro::TokenStream,
2426    item: proc_macro::TokenStream,
2427) -> proc_macro::TokenStream {
2428    let foreign_mod = match ::syn::parse::<syn::ItemForeignMod>(item) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(item as syn::ItemForeignMod);
2429
2430    let foreign_mod_attrs = &foreign_mod.attrs;
2431    let abi = &foreign_mod.abi;
2432    let mut unchecked_items = Vec::new();
2433    let mut checked_wrappers = Vec::new();
2434
2435    for item in &foreign_mod.items {
2436        match item {
2437            syn::ForeignItem::Fn(fn_item) => {
2438                let is_variadic = fn_item.sig.variadic.is_some();
2439
2440                // Check if function already has #[link_name] - if so, pass through unchanged
2441                let has_link_name = fn_item
2442                    .attrs
2443                    .iter()
2444                    .any(|attr| attr.path().is_ident("link_name"));
2445
2446                if is_variadic || has_link_name {
2447                    // Pass through variadic functions and functions with explicit link_name unchanged
2448                    unchecked_items.push(item.clone());
2449                } else {
2450                    // Generate checked wrapper for non-variadic functions
2451                    let vis = &fn_item.vis;
2452                    let fn_name = &fn_item.sig.ident;
2453                    let fn_name_str = fn_name.to_string();
2454                    let unchecked_name = match ::quote::__private::IdentFragmentAdapter(&fn_name) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("{0}_unchecked", arg))
                    }), ::quote::__private::Option::None.or(arg.span())),
}quote::format_ident!("{}_unchecked", fn_name);
2455                    let unchecked_name_str = unchecked_name.to_string();
2456                    let inputs = &fn_item.sig.inputs;
2457                    let output = &fn_item.sig.output;
2458                    // Filter out link_name attributes (already checked above, but be safe)
2459                    let attrs: Vec<_> = fn_item
2460                        .attrs
2461                        .iter()
2462                        .filter(|attr| !attr.path().is_ident("link_name"))
2463                        .collect();
2464                    let checked_doc = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Checked wrapper for `{0}`. Calls `{1}` and routes through `with_r_thread`.",
                fn_name_str, unchecked_name_str))
    })format!(
2465                        "Checked wrapper for `{}`. Calls `{}` and routes through `with_r_thread`.",
2466                        fn_name_str, unchecked_name_str
2467                    );
2468                    let checked_doc_lit = syn::LitStr::new(&checked_doc, fn_name.span());
2469                    let source_loc_doc = crate::source_location_doc(fn_name.span());
2470                    let source_loc_doc_lit = syn::LitStr::new(&source_loc_doc, fn_name.span());
2471
2472                    // Generate the unchecked FFI binding with #[link_name]
2473                    // Same visibility as the checked variant
2474                    let link_name = syn::LitStr::new(&fn_name_str, fn_name.span());
2475                    let unchecked_fn: syn::ForeignItem = ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        {
            use ::quote::__private::ext::*;
            let has_iter = ::quote::__private::HasIterator::<false>;
            #[allow(unused_mut)]
            let (mut attrs, i) = attrs.quote_into_iter();
            let has_iter = has_iter | i;
            <_ as
                    ::quote::__private::CheckHasIterator<true>>::check(has_iter);
            while true {
                let attrs =
                    match attrs.next() {
                        Some(_x) => ::quote::__private::RepInterp(_x),
                        None => break,
                    };
                ::quote::ToTokens::to_tokens(&attrs, &mut _s);
            }
        }
        ::quote::__private::push_pound(&mut _s);
        ::quote::__private::push_group(&mut _s,
            ::quote::__private::Delimiter::Bracket,
            {
                let mut _s = ::quote::__private::TokenStream::new();
                ::quote::__private::push_ident(&mut _s, "doc");
                ::quote::__private::push_eq(&mut _s);
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "core");
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "concat");
                ::quote::__private::push_bang(&mut _s);
                ::quote::__private::push_group(&mut _s,
                    ::quote::__private::Delimiter::Parenthesis,
                    {
                        let mut _s = ::quote::__private::TokenStream::new();
                        ::quote::__private::parse(&mut _s,
                            "\"Unchecked FFI binding for `\"");
                        ::quote::__private::push_comma(&mut _s);
                        ::quote::__private::push_colon2(&mut _s);
                        ::quote::__private::push_ident(&mut _s, "core");
                        ::quote::__private::push_colon2(&mut _s);
                        ::quote::__private::push_ident(&mut _s, "stringify");
                        ::quote::__private::push_bang(&mut _s);
                        ::quote::__private::push_group(&mut _s,
                            ::quote::__private::Delimiter::Parenthesis,
                            {
                                let mut _s = ::quote::__private::TokenStream::new();
                                ::quote::ToTokens::to_tokens(&fn_name, &mut _s);
                                _s
                            });
                        ::quote::__private::push_comma(&mut _s);
                        ::quote::__private::parse(&mut _s, "\"`.\"");
                        _s
                    });
                _s
            });
        ::quote::__private::push_pound(&mut _s);
        ::quote::__private::push_group(&mut _s,
            ::quote::__private::Delimiter::Bracket,
            {
                let mut _s = ::quote::__private::TokenStream::new();
                ::quote::__private::push_ident(&mut _s, "doc");
                ::quote::__private::push_eq(&mut _s);
                ::quote::ToTokens::to_tokens(&source_loc_doc_lit, &mut _s);
                _s
            });
        ::quote::__private::push_pound(&mut _s);
        ::quote::__private::push_group(&mut _s,
            ::quote::__private::Delimiter::Bracket,
            {
                let mut _s = ::quote::__private::TokenStream::new();
                ::quote::__private::push_ident(&mut _s, "doc");
                ::quote::__private::push_eq(&mut _s);
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "core");
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "concat");
                ::quote::__private::push_bang(&mut _s);
                ::quote::__private::push_group(&mut _s,
                    ::quote::__private::Delimiter::Parenthesis,
                    {
                        let mut _s = ::quote::__private::TokenStream::new();
                        ::quote::__private::parse(&mut _s,
                            "\"Generated from source file `\"");
                        ::quote::__private::push_comma(&mut _s);
                        ::quote::__private::push_colon2(&mut _s);
                        ::quote::__private::push_ident(&mut _s, "core");
                        ::quote::__private::push_colon2(&mut _s);
                        ::quote::__private::push_ident(&mut _s, "file");
                        ::quote::__private::push_bang(&mut _s);
                        ::quote::__private::push_group(&mut _s,
                            ::quote::__private::Delimiter::Parenthesis,
                            ::quote::__private::TokenStream::new());
                        ::quote::__private::push_comma(&mut _s);
                        ::quote::__private::parse(&mut _s, "\"`.\"");
                        _s
                    });
                _s
            });
        ::quote::__private::push_pound(&mut _s);
        ::quote::__private::push_group(&mut _s,
            ::quote::__private::Delimiter::Bracket,
            {
                let mut _s = ::quote::__private::TokenStream::new();
                ::quote::__private::push_ident(&mut _s, "link_name");
                ::quote::__private::push_eq(&mut _s);
                ::quote::ToTokens::to_tokens(&link_name, &mut _s);
                _s
            });
        ::quote::ToTokens::to_tokens(&vis, &mut _s);
        ::quote::__private::push_ident(&mut _s, "fn");
        ::quote::ToTokens::to_tokens(&unchecked_name, &mut _s);
        ::quote::__private::push_group(&mut _s,
            ::quote::__private::Delimiter::Parenthesis,
            {
                let mut _s = ::quote::__private::TokenStream::new();
                ::quote::ToTokens::to_tokens(&inputs, &mut _s);
                _s
            });
        ::quote::ToTokens::to_tokens(&output, &mut _s);
        ::quote::__private::push_semi(&mut _s);
        _s
    })syn::parse_quote! {
2476                        #(#attrs)*
2477                        #[doc = ::core::concat!("Unchecked FFI binding for `", ::core::stringify!(#fn_name), "`.")]
2478                        #[doc = #source_loc_doc_lit]
2479                        #[doc = ::core::concat!("Generated from source file `", ::core::file!(), "`.")]
2480                        #[link_name = #link_name]
2481                        #vis fn #unchecked_name(#inputs) #output;
2482                    };
2483                    unchecked_items.push(unchecked_fn);
2484
2485                    // Generate a checked wrapper function
2486                    let arg_names: Vec<_> = inputs
2487                        .iter()
2488                        .filter_map(|arg| {
2489                            if let syn::FnArg::Typed(pat_type) = arg
2490                                && let syn::Pat::Ident(pat_ident) = pat_type.pat.as_ref()
2491                            {
2492                                Some(pat_ident.ident.clone())
2493                            } else {
2494                                None
2495                            }
2496                        })
2497                        .collect();
2498
2499                    let is_never = #[allow(non_exhaustive_omitted_patterns)] match output {
    syn::ReturnType::Type(_, ty) if
        #[allow(non_exhaustive_omitted_patterns)] match **ty {
            syn::Type::Never(_) => true,
            _ => false,
        } => true,
    _ => false,
}matches!(output, syn::ReturnType::Type(_, ty) if matches!(**ty, syn::Type::Never(_)));
2500
2501                    let wrapper = if is_never {
2502                        // Never-returning functions (like Rf_error)
2503                        {
    let mut _s = ::quote::__private::TokenStream::new();
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut attrs, i) = attrs.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let attrs =
                match attrs.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&attrs, &mut _s);
        }
    }
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::ToTokens::to_tokens(&checked_doc_lit, &mut _s);
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::ToTokens::to_tokens(&source_loc_doc_lit, &mut _s);
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "concat");
            ::quote::__private::push_bang(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::parse(&mut _s,
                        "\"Generated from source file `\"");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "file");
                    ::quote::__private::push_bang(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\"`.\"");
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "inline");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "always");
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "non_snake_case");
                    _s
                });
            _s
        });
    ::quote::ToTokens::to_tokens(&vis, &mut _s);
    ::quote::__private::push_ident(&mut _s, "unsafe");
    ::quote::__private::push_ident(&mut _s, "fn");
    ::quote::ToTokens::to_tokens(&fn_name, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&inputs, &mut _s);
            _s
        });
    ::quote::ToTokens::to_tokens(&output, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "worker");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "with_r_thread");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "move");
                    ::quote::__private::push_or_or(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "unsafe");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::ToTokens::to_tokens(&unchecked_name, &mut _s);
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    {
                                        use ::quote::__private::ext::*;
                                        let mut _first = true;
                                        let has_iter = ::quote::__private::HasIterator::<false>;
                                        #[allow(unused_mut)]
                                        let (mut arg_names, i) = arg_names.quote_into_iter();
                                        let has_iter = has_iter | i;
                                        <_ as
                                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                        while true {
                                            let arg_names =
                                                match arg_names.next() {
                                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                                    None => break,
                                                };
                                            if !_first { ::quote::__private::push_comma(&mut _s); }
                                            _first = false;
                                            ::quote::ToTokens::to_tokens(&arg_names, &mut _s);
                                        }
                                    }
                                    _s
                                });
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote::quote! {
2504                            #(#attrs)*
2505                            #[doc = #checked_doc_lit]
2506                            #[doc = #source_loc_doc_lit]
2507                            #[doc = ::core::concat!("Generated from source file `", ::core::file!(), "`.")]
2508                            #[inline(always)]
2509                            #[allow(non_snake_case)]
2510                            #vis unsafe fn #fn_name(#inputs) #output {
2511                                ::miniextendr_api::worker::with_r_thread(move || unsafe {
2512                                    #unchecked_name(#(#arg_names),*)
2513                                })
2514                            }
2515                        }
2516                    } else {
2517                        // Normal functions - route via with_r_thread
2518                        {
    let mut _s = ::quote::__private::TokenStream::new();
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut attrs, i) = attrs.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let attrs =
                match attrs.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&attrs, &mut _s);
        }
    }
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::ToTokens::to_tokens(&checked_doc_lit, &mut _s);
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::ToTokens::to_tokens(&source_loc_doc_lit, &mut _s);
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "concat");
            ::quote::__private::push_bang(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::parse(&mut _s,
                        "\"Generated from source file `\"");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "file");
                    ::quote::__private::push_bang(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\"`.\"");
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "inline");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "always");
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "non_snake_case");
                    _s
                });
            _s
        });
    ::quote::ToTokens::to_tokens(&vis, &mut _s);
    ::quote::__private::push_ident(&mut _s, "unsafe");
    ::quote::__private::push_ident(&mut _s, "fn");
    ::quote::ToTokens::to_tokens(&fn_name, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&inputs, &mut _s);
            _s
        });
    ::quote::ToTokens::to_tokens(&output, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "let");
            ::quote::__private::push_ident(&mut _s, "result");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "worker");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "with_r_thread");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "move");
                    ::quote::__private::push_or_or(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "worker");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "Sendable");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_ident(&mut _s, "unsafe");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Brace,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::ToTokens::to_tokens(&unchecked_name, &mut _s);
                                            ::quote::__private::push_group(&mut _s,
                                                ::quote::__private::Delimiter::Parenthesis,
                                                {
                                                    let mut _s = ::quote::__private::TokenStream::new();
                                                    {
                                                        use ::quote::__private::ext::*;
                                                        let mut _first = true;
                                                        let has_iter = ::quote::__private::HasIterator::<false>;
                                                        #[allow(unused_mut)]
                                                        let (mut arg_names, i) = arg_names.quote_into_iter();
                                                        let has_iter = has_iter | i;
                                                        <_ as
                                                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                                        while true {
                                                            let arg_names =
                                                                match arg_names.next() {
                                                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                                                    None => break,
                                                                };
                                                            if !_first { ::quote::__private::push_comma(&mut _s); }
                                                            _first = false;
                                                            ::quote::ToTokens::to_tokens(&arg_names, &mut _s);
                                                        }
                                                    }
                                                    _s
                                                });
                                            _s
                                        });
                                    _s
                                });
                            _s
                        });
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "result");
            ::quote::__private::push_dot(&mut _s);
            ::quote::__private::parse(&mut _s, "0");
            _s
        });
    _s
}quote::quote! {
2519                            #(#attrs)*
2520                            #[doc = #checked_doc_lit]
2521                            #[doc = #source_loc_doc_lit]
2522                            #[doc = ::core::concat!("Generated from source file `", ::core::file!(), "`.")]
2523                            #[inline(always)]
2524                            #[allow(non_snake_case)]
2525                            #vis unsafe fn #fn_name(#inputs) #output {
2526                                let result = ::miniextendr_api::worker::with_r_thread(move || {
2527                                    ::miniextendr_api::worker::Sendable(unsafe {
2528                                        #unchecked_name(#(#arg_names),*)
2529                                    })
2530                                });
2531                                result.0
2532                            }
2533                        }
2534                    };
2535                    checked_wrappers.push(wrapper);
2536                }
2537            }
2538            _ => {
2539                // Pass through statics and other items unchanged
2540                unchecked_items.push(item.clone());
2541            }
2542        }
2543    }
2544
2545    let expanded = {
    let mut _s = ::quote::__private::TokenStream::new();
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut foreign_mod_attrs, i) = foreign_mod_attrs.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let foreign_mod_attrs =
                match foreign_mod_attrs.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&foreign_mod_attrs, &mut _s);
        }
    }
    ::quote::__private::push_ident(&mut _s, "unsafe");
    ::quote::ToTokens::to_tokens(&abi, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            {
                use ::quote::__private::ext::*;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut unchecked_items, i) =
                    unchecked_items.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let unchecked_items =
                        match unchecked_items.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&unchecked_items, &mut _s);
                }
            }
            _s
        });
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut checked_wrappers, i) = checked_wrappers.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let checked_wrappers =
                match checked_wrappers.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&checked_wrappers, &mut _s);
        }
    }
    _s
}quote::quote! {
2546        #(#foreign_mod_attrs)*
2547        unsafe #abi {
2548            #(#unchecked_items)*
2549        }
2550
2551        #(#checked_wrappers)*
2552    };
2553
2554    expanded.into()
2555}
2556
2557/// Derive macro for implementing `RNativeType` on a newtype wrapper.
2558///
2559/// This gives newtypes native buffer access, borrowed `&[T]` conversions, and
2560/// participation in the `Coerce<R>` traits. Add `#[derive(IntoR)]` to return
2561/// owned values: `T`, `Vec<T>`, `Box<[T]>`, and their supported nested containers.
2562/// `RNativeType` describes storage; `IntoR` selects the output representation.
2563/// Keeping them separate also permits an `ExternalPtr` representation.
2564/// The inner type must implement `RNativeType`. Add `#[derive(IsNA)]`
2565/// to forward missing-value detection when the inner type supports NA.
2566///
2567/// # Supported Struct Forms
2568///
2569/// Both tuple structs and single-field named structs are supported:
2570///
2571/// ```ignore
2572/// use miniextendr_api::RNativeType;
2573///
2574/// // Tuple struct (most common)
2575/// #[repr(transparent)]
2576/// #[derive(Clone, Copy, RNativeType)]
2577/// struct UserId(i32);
2578///
2579/// // Named single-field struct
2580/// #[repr(transparent)]
2581/// #[derive(Clone, Copy, RNativeType)]
2582/// struct Temperature { celsius: f64 }
2583/// ```
2584///
2585/// # Generated Code
2586///
2587/// For `struct UserId(i32)`, this generates:
2588///
2589/// ```ignore
2590/// impl RNativeType for UserId {
2591///     const SEXP_TYPE: SEXPTYPE = <i32 as RNativeType>::SEXP_TYPE;
2592///     const R_NA: Self = UserId(<i32 as RNativeType>::R_NA);
2593///
2594///     unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self {
2595///         <i32 as RNativeType>::dataptr_mut(sexp).cast()
2596///     }
2597/// }
2598/// ```
2599///
2600/// # Using the Newtype with Coerce
2601///
2602/// Once `RNativeType` is derived, you can implement `Coerce` to/from the newtype:
2603///
2604/// ```ignore
2605/// impl Coerce<UserId> for i32 {
2606///     fn coerce(self) -> UserId { UserId(self) }
2607/// }
2608///
2609/// let id: UserId = 42.coerce();
2610/// ```
2611///
2612/// # Requirements
2613///
2614/// - Must be a newtype struct (exactly one field, tuple or named)
2615/// - Must use `#[repr(transparent)]` so native R buffers have the same layout
2616/// - The inner type must implement `RNativeType` (`i32`, `f64`, `RLogical`, `u8`, `Rcomplex`)
2617/// - Must also be `Copy` (`RNativeType: Copy`): derive `Clone, Copy` with it
2618#[proc_macro_derive(RNativeType)]
2619pub fn derive_rnative_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2620    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
2621    let name = &input.ident;
2622    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
2623
2624    // Extract inner type and constructor — must be a newtype (single field)
2625    let (inner_ty, elt_ctor): (syn::Type, proc_macro2::TokenStream) = match &input.data {
2626        syn::Data::Struct(data) => match &data.fields {
2627            syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
2628                let ty = fields.unnamed.first().unwrap().ty.clone();
2629                let ctor = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Self");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "val");
            _s
        });
    _s
}quote::quote! { Self(val) };
2630                (ty, ctor)
2631            }
2632            syn::Fields::Named(fields) if fields.named.len() == 1 => {
2633                let field = fields.named.first().unwrap();
2634                let ty = field.ty.clone();
2635                let field_name = field.ident.as_ref().unwrap();
2636                let ctor = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Self");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&field_name, &mut _s);
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_ident(&mut _s, "val");
            _s
        });
    _s
}quote::quote! { Self { #field_name: val } };
2637                (ty, ctor)
2638            }
2639            _ => {
2640                return syn::Error::new_spanned(
2641                    name,
2642                    "#[derive(RNativeType)] requires a newtype struct with exactly one field",
2643                )
2644                .into_compile_error()
2645                .into();
2646            }
2647        },
2648        _ => {
2649            return syn::Error::new_spanned(name, "#[derive(RNativeType)] only works on structs")
2650                .into_compile_error()
2651                .into();
2652        }
2653    };
2654
2655    // RNativeType exposes slices of Self over the inner type's R allocation.
2656    // A one-field Rust struct alone does not guarantee the required layout.
2657    let transparent = input
2658        .attrs
2659        .iter()
2660        .filter(|attr| attr.path().is_ident("repr"))
2661        .any(|attr| {
2662            attr.parse_args_with(
2663                syn::punctuated::Punctuated::<syn::Meta, ::syn::token::Commasyn::Token![,]>::parse_terminated,
2664            )
2665            .is_ok_and(|reprs| reprs.iter().any(|repr| repr.path().is_ident("transparent")))
2666        });
2667    if !transparent {
2668        return syn::Error::new_spanned(
2669            name,
2670            "#[derive(RNativeType)] requires #[repr(transparent)] for native R buffer access",
2671        )
2672        .into_compile_error()
2673        .into();
2674    }
2675
2676    let expanded = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "RNativeType");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "const");
            ::quote::__private::push_ident(&mut _s, "SEXP_TYPE");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SEXPTYPE");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_lt(&mut _s);
            ::quote::ToTokens::to_tokens(&inner_ty, &mut _s);
            ::quote::__private::push_ident(&mut _s, "as");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "RNativeType");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "SEXP_TYPE");
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "const");
            ::quote::__private::push_ident(&mut _s, "R_NA");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "val");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::ToTokens::to_tokens(&inner_ty, &mut _s);
                    ::quote::__private::push_ident(&mut _s, "as");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "RNativeType");
                    ::quote::__private::push_gt(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "R_NA");
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::ToTokens::to_tokens(&elt_ctor, &mut _s);
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_pound(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "inline");
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "unsafe");
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "dataptr_mut");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "sexp");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "SEXP");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_star(&mut _s);
            ::quote::__private::push_ident(&mut _s, "mut");
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "unsafe");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_lt(&mut _s);
                            ::quote::ToTokens::to_tokens(&inner_ty, &mut _s);
                            ::quote::__private::push_ident(&mut _s, "as");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "RNativeType");
                            ::quote::__private::push_gt(&mut _s);
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "dataptr_mut");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_ident(&mut _s, "sexp");
                                    _s
                                });
                            ::quote::__private::push_dot(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "cast");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                ::quote::__private::TokenStream::new());
                            _s
                        });
                    _s
                });
            ::quote::__private::push_pound(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "inline");
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "elt");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "sexp");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "SEXP");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "i");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "isize");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "val");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::ToTokens::to_tokens(&inner_ty, &mut _s);
                    ::quote::__private::push_ident(&mut _s, "as");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "RNativeType");
                    ::quote::__private::push_gt(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "elt");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "sexp");
                            ::quote::__private::push_comma(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "i");
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::ToTokens::to_tokens(&elt_ctor, &mut _s);
                    _s
                });
            _s
        });
    _s
}quote::quote! {
2677        impl #impl_generics ::miniextendr_api::RNativeType for #name #ty_generics #where_clause {
2678            const SEXP_TYPE: ::miniextendr_api::SEXPTYPE =
2679                <#inner_ty as ::miniextendr_api::RNativeType>::SEXP_TYPE;
2680
2681            const R_NA: Self = {
2682                let val = <#inner_ty as ::miniextendr_api::RNativeType>::R_NA;
2683                #elt_ctor
2684            };
2685
2686            #[inline]
2687            unsafe fn dataptr_mut(sexp: ::miniextendr_api::SEXP) -> *mut Self {
2688                // Newtype is repr(transparent), so we can cast the pointer
2689                unsafe {
2690                    <#inner_ty as ::miniextendr_api::RNativeType>::dataptr_mut(sexp).cast()
2691                }
2692            }
2693
2694            #[inline]
2695            fn elt(sexp: ::miniextendr_api::SEXP, i: isize) -> Self {
2696                let val = <#inner_ty as ::miniextendr_api::RNativeType>::elt(sexp, i);
2697                #elt_ctor
2698            }
2699        }
2700
2701    };
2702
2703    expanded.into()
2704}
2705
2706/// Derive `IsNA` for a native newtype by forwarding to its single field.
2707///
2708/// Supports tuple, named and generic newtypes that also implement
2709/// `RNativeType`. The inner field must implement `IsNA`; existing where
2710/// clauses are preserved. This derive is separate from `RNativeType`
2711/// because raw `u8` storage has no NA representation.
2712///
2713/// ```
2714/// use miniextendr_api::{IsNA, RNativeType};
2715///
2716/// #[repr(transparent)]
2717/// #[derive(Clone, Copy, RNativeType, IsNA)]
2718/// struct Temperature { celsius: f64 }
2719///
2720/// assert!(Temperature::R_NA.is_na());
2721/// assert!(!Temperature { celsius: f64::NAN }.is_na());
2722/// ```
2723///
2724/// A generic native wrapper supports raw bytes too, but its `IsNA` impl is
2725/// available only when the inner type has NA:
2726///
2727/// ```compile_fail
2728/// use miniextendr_api::{IsNA, RNativeType};
2729///
2730/// #[repr(transparent)]
2731/// #[derive(Clone, Copy, RNativeType, IsNA)]
2732/// struct Native<T: RNativeType>(T);
2733///
2734/// fn requires_na<T: IsNA>() {}
2735/// requires_na::<Native<u8>>();
2736/// ```
2737#[proc_macro_derive(IsNA)]
2738pub fn derive_is_na(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2739    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
2740    newtype_derive::derive_is_na(input)
2741        .unwrap_or_else(|e| e.into_compile_error())
2742        .into()
2743}
2744
2745/// Derive macro for implementing `TypedExternal` on a type.
2746///
2747/// This makes the type compatible with `ExternalPtr<T>` for storing in R's external pointers.
2748///
2749/// # Basic Usage
2750///
2751/// ```ignore
2752/// use miniextendr_api::TypedExternal;
2753///
2754/// #[derive(ExternalPtr)]
2755/// struct MyData {
2756///     value: i32,
2757/// }
2758///
2759/// // Now you can use ExternalPtr<MyData>
2760/// let ptr = ExternalPtr::new(MyData { value: 42 });
2761/// ```
2762///
2763/// # Trait ABI
2764///
2765/// Trait dispatch wrappers are automatically generated:
2766///
2767/// ```ignore
2768/// use miniextendr_api::miniextendr;
2769///
2770/// #[derive(ExternalPtr)]
2771/// struct MyCounter {
2772///     value: i32,
2773/// }
2774///
2775/// #[miniextendr]
2776/// impl Counter for MyCounter {
2777///     fn value(&self) -> i32 { self.value }
2778///     fn increment(&mut self) { self.value += 1; }
2779/// }
2780/// ```
2781///
2782/// This generates additional infrastructure for type-erased trait dispatch:
2783/// - Shared runtime `ErasedObject<MyCounter>` storage and destructor
2784/// - `__MX_BASE_VTABLE_MyCounter` - Base vtable with drop/query
2785/// - `__mx_wrap_MyCounter()` - Constructor returning `*mut mx_erased`
2786///
2787/// # Generated Code (Basic)
2788///
2789/// For a type `MyData` without traits:
2790///
2791/// ```ignore
2792/// impl TypedExternal for MyData {
2793///     const TYPE_NAME: &'static str = "MyData";
2794///     const TYPE_NAME_CSTR: &'static [u8] = b"MyData\0";
2795/// }
2796/// ```
2797#[proc_macro_derive(ExternalPtr, attributes(externalptr, r_data))]
2798pub fn derive_external_ptr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2799    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
2800
2801    // Standalone `#[derive(ExternalPtr)]` always emits the `IntoExternalPtr`
2802    // marker (enabling the blanket `IntoR`); only the struct-level
2803    // `prefer = "native"` dispatch path suppresses it (#1283).
2804    externalptr_derive::derive_external_ptr(input, true)
2805        .unwrap_or_else(|e| e.into_compile_error())
2806        .into()
2807}
2808
2809/// Derive an `all.equal()` method for the R class of an `ExternalPtr` type,
2810/// comparing the struct field by field (#2149).
2811///
2812/// Without it, `all.equal()` on two objects of a pointer-backed class
2813/// returns `TRUE` whatever they hold: base R compares an external pointer
2814/// only by its `data.class()`. The derived method compares each field with
2815/// base `all.equal()` on the field's R value, so each field keeps its own
2816/// method: numbers keep R's tolerance, lists and data frames use base's
2817/// methods, and a field holding another pointer class `T` uses `T`'s own
2818/// `all.equal()` method (another `AllEqual` type).
2819///
2820/// ```ignore
2821/// #[derive(ExternalPtr, AllEqual)]
2822/// #[all_equal(tolerance = 1e-6)]
2823/// pub struct Fit {
2824///     #[all_equal(label = "Estimates")]
2825///     estimates: Vec<f64>,
2826///     #[all_equal(tolerance = 0)]
2827///     iterations: i32,
2828///     model: ExternalPtr<Model>,
2829///     #[all_equal(skip)]
2830///     cache: RefCell<Option<Vec<f64>>>,
2831/// }
2832///
2833/// #[miniextendr(s3)]
2834/// impl Fit { /* ... */ }
2835/// ```
2836///
2837/// ```r
2838/// all.equal(fit_a, fit_b)                    # TRUE, or c("Estimates: Mean relative difference: 2e-05", ...)
2839/// all.equal(fit_a, fit_b, tolerance = 1e-3)  # the call's tolerance replaces the struct's default
2840/// ```
2841///
2842/// The type also needs `#[derive(ExternalPtr)]` and an impl block with a
2843/// class system (`env`, `r6`, `s3`, `s4` or `s7`): the method is
2844/// `all.equal.<Class>`, registered with `S3method()`, and for S7 also with
2845/// `S7::method(all.equal, Class)`. Installing a package with the derive on a
2846/// type without a class, or with a vctrs class (whose objects are R vectors
2847/// that `all.equal()` already compares), fails with a message naming it.
2848///
2849/// # The comparison
2850///
2851/// The method is `all.equal.<Class>(target, current, tolerance, scale, ...,
2852/// check.attributes, check.names)`, in `all.equal.numeric()`'s argument
2853/// order. A `current` of another class gives base's
2854/// `"target is <class>, current is <class>"`. Otherwise each compared field
2855/// is compared with `all.equal(target_field, current_field, ...)`, and the
2856/// result is `TRUE` or the messages of every differing field, in field
2857/// order, each prefixed as `all.equal.list()` does (`Component “estimates”:
2858/// ...`, `Component 1: ...` for a tuple field) or with the field's `label`.
2859/// An object restored from a saved session (`readRDS()`) is refused with
2860/// the usual `miniextendr_restored_*` error.
2861///
2862/// A field's R value is a clone converted with `IntoR`, or:
2863///
2864/// - `#[all_equal(with = "Self::f")]`: the value of a getter
2865///   `fn(&Self) -> impl IntoR`;
2866/// - a `Computed` field: the value of its `#[r_data(get = "...")]` getter;
2867/// - a `Sidecar<T>` field: its value in the pointer's protection list;
2868/// - an `ExternalPtr<T>` or `Option<ExternalPtr<T>>` field: the pointer
2869///   itself, not a clone.
2870///
2871/// The `RSidecar` selector is not compared.
2872///
2873/// A field whose type names a pointer class `T` of the package (`T`,
2874/// `ExternalPtr<T>`, `Option<ExternalPtr<T>>`, or a `Vec` of `T` or
2875/// `ExternalPtr<T>`) has its value given `T`'s R class, as a method returning that type does, so `T`'s own
2876/// method compares it: give `T` the derive too, since without a method of
2877/// its own `T` is compared by `all.equal.default()`, which finds any two
2878/// pointers equal. The derive can't see a getter's type, so a pointer a
2879/// `with` getter or a `Computed` field returns stays bare, as does a class
2880/// of another package (#2191).
2881///
2882/// # Settings
2883///
2884/// `tolerance`, `scale`, `check_attributes` and `check_names` (the R
2885/// arguments `check.attributes` / `check.names`) are formals of the method.
2886/// Each is passed to a field's `all.equal()` only when something sets it, so
2887/// a field's own method otherwise keeps its default (a nested `AllEqual`
2888/// type its own struct-level settings). Precedence: the field's
2889/// `#[all_equal(...)]`, then the call, then the struct's `#[all_equal(...)]`
2890/// (the formal's default), then the field's own method's default. Other
2891/// arguments of the call are passed on to every field through `...`.
2892///
2893/// On the struct, `#[all_equal(...)]` takes the settings. On a field it
2894/// takes the settings, which the call can't override, and:
2895///
2896/// - `skip`: the field is not compared (alone: it takes no other option);
2897/// - `label = "..."`: the field's messages start with `"<label>: "`;
2898/// - `with = "Self::f"`: the getter whose value is compared.
2899#[proc_macro_derive(AllEqual, attributes(all_equal))]
2900pub fn derive_all_equal(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2901    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
2902    all_equal_derive::derive_all_equal(input)
2903        .unwrap_or_else(|e| e.into_compile_error())
2904        .into()
2905}
2906
2907/// Derive macro for ALTREP integer vector data types.
2908///
2909/// Auto-implements `AltrepLen`, `AltIntegerData`, and the low-level ALTREP
2910/// trait impls (`Altrep`, `AltVec`, `AltInteger`, `InferBase`).
2911///
2912/// # Attributes
2913///
2914/// - `#[altrep(len = "field_name")]` - Specify length field (auto-detects "len" or "length")
2915/// - `#[altrep(elt = "field_name")]` - For constant vectors, specify which field provides elements
2916/// - `#[altrep(dataptr)]` - Enable direct data-pointer access
2917/// - `#[altrep(serialize)]` - Enable ALTREP serialization support
2918/// - `#[altrep(subset)]` - Enable `Extract_subset` optimization
2919/// - `#[altrep(no_lowlevel)]` - Skip the automatic low-level trait impls
2920///
2921/// # Example (Constant Vector - Zero Boilerplate!)
2922///
2923/// ```ignore
2924/// #[derive(ExternalPtr, AltrepInteger)]
2925/// #[altrep(elt = "value")]  // All elements return this field
2926/// pub struct ConstantIntData {
2927///     value: i32,
2928///     len: usize,
2929/// }
2930///
2931/// // That's it! 3 lines instead of 30!
2932/// // AltrepLen, AltIntegerData, and low-level impls are auto-generated
2933///
2934/// #[miniextendr(class = "ConstantInt")]
2935/// pub struct ConstantIntClass(pub ConstantIntData);
2936/// ```
2937///
2938/// # Example (Custom elt() - Override One Method)
2939///
2940/// ```ignore
2941/// #[derive(ExternalPtr, AltrepInteger)]
2942/// pub struct ArithSeqData {
2943///     start: i32,
2944///     step: i32,
2945///     len: usize,
2946/// }
2947///
2948/// // Auto-generates AltrepLen and stub AltIntegerData
2949/// // Just override elt() for custom logic:
2950/// impl AltIntegerData for ArithSeqData {
2951///     fn elt(&self, i: usize) -> i32 {
2952///         self.start + (i as i32) * self.step
2953///     }
2954/// }
2955/// ```
2956#[proc_macro_derive(AltrepInteger, attributes(altrep, r_data))]
2957pub fn derive_altrep_integer(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2958    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
2959    altrep_derive::derive_altrep_integer(input)
2960        .unwrap_or_else(|e| e.into_compile_error())
2961        .into()
2962}
2963
2964/// Derive macro for ALTREP real vector data types.
2965///
2966/// Auto-implements `AltrepLen` and `AltRealData` traits.
2967/// Supports the same `#[altrep(...)]` attributes as [`AltrepInteger`](derive@AltrepInteger).
2968#[proc_macro_derive(AltrepReal, attributes(altrep, r_data))]
2969pub fn derive_altrep_real(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2970    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
2971    altrep_derive::derive_altrep_real(input)
2972        .unwrap_or_else(|e| e.into_compile_error())
2973        .into()
2974}
2975
2976/// Derive macro for ALTREP logical vector data types.
2977///
2978/// Auto-implements `AltrepLen` and `AltLogicalData` traits.
2979/// Supports the same `#[altrep(...)]` attributes as [`AltrepInteger`](derive@AltrepInteger).
2980#[proc_macro_derive(AltrepLogical, attributes(altrep, r_data))]
2981pub fn derive_altrep_logical(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2982    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
2983    altrep_derive::derive_altrep_logical(input)
2984        .unwrap_or_else(|e| e.into_compile_error())
2985        .into()
2986}
2987
2988/// Derive macro for ALTREP raw vector data types.
2989///
2990/// Auto-implements `AltrepLen` and `AltRawData` traits.
2991/// Supports the same `#[altrep(...)]` attributes as [`AltrepInteger`](derive@AltrepInteger).
2992#[proc_macro_derive(AltrepRaw, attributes(altrep, r_data))]
2993pub fn derive_altrep_raw(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2994    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
2995    altrep_derive::derive_altrep_raw(input)
2996        .unwrap_or_else(|e| e.into_compile_error())
2997        .into()
2998}
2999
3000/// Derive macro for ALTREP string vector data types.
3001///
3002/// Auto-implements `AltrepLen` and `AltStringData` traits.
3003/// Supports the same `#[altrep(...)]` attributes as [`AltrepInteger`](derive@AltrepInteger).
3004#[proc_macro_derive(AltrepString, attributes(altrep, r_data))]
3005pub fn derive_altrep_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3006    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3007    altrep_derive::derive_altrep_string(input)
3008        .unwrap_or_else(|e| e.into_compile_error())
3009        .into()
3010}
3011
3012/// Derive macro for ALTREP complex vector data types.
3013///
3014/// Auto-implements `AltrepLen` and `AltComplexData` traits.
3015/// Supports the same `#[altrep(...)]` attributes as [`AltrepInteger`](derive@AltrepInteger).
3016#[proc_macro_derive(AltrepComplex, attributes(altrep, r_data))]
3017pub fn derive_altrep_complex(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3018    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3019    altrep_derive::derive_altrep_complex(input)
3020        .unwrap_or_else(|e| e.into_compile_error())
3021        .into()
3022}
3023
3024/// Derive macro for ALTREP list vector data types.
3025///
3026/// Auto-implements `AltrepLen` and `AltListData` traits.
3027/// Supports the same `#[altrep(...)]` attributes as [`AltrepInteger`](derive@AltrepInteger),
3028/// except `dataptr` and `subset` which are not supported for list ALTREP.
3029///
3030/// The generated class accepts R's writes (`x[[i]] <- v`): the first one copies
3031/// the elements into a plain list in data2, which element reads use from then on.
3032#[proc_macro_derive(AltrepList, attributes(altrep, r_data))]
3033pub fn derive_altrep_list(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3034    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3035    altrep_derive::derive_altrep_list(input)
3036        .unwrap_or_else(|e| e.into_compile_error())
3037        .into()
3038}
3039
3040/// Derive ALTREP registration for a data struct.
3041///
3042/// Generates `TypedExternal`, `AltrepClass`, `RegisterAltrep`, `IntoR`,
3043/// linkme registration entry, and `Ref`/`Mut` accessor types.
3044///
3045/// The struct must already have low-level ALTREP traits implemented.
3046/// For most use cases, prefer a family-specific derive:
3047/// `#[derive(AltrepInteger)]`, `#[derive(AltrepReal)]`, etc.
3048/// Use `#[altrep(manual)]` on a family derive to skip data trait generation
3049/// when you provide your own `AltrepLen` + `Alt*Data` impls.
3050///
3051/// # Attributes
3052///
3053/// - `#[altrep(class = "Name")]` — custom ALTREP class name (defaults to struct name)
3054///
3055/// # Example
3056///
3057/// ```ignore
3058/// // Prefer family derives with manual:
3059/// #[derive(AltrepInteger)]
3060/// #[altrep(manual, class = "MyCustom", serialize)]
3061/// struct MyData { ... }
3062///
3063/// impl AltrepLen for MyData { ... }
3064/// impl AltIntegerData for MyData { ... }
3065/// ```
3066#[proc_macro_derive(Altrep, attributes(altrep, r_data))]
3067pub fn derive_altrep(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3068    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3069    altrep::derive_altrep(input)
3070        .unwrap_or_else(|e| e.into_compile_error())
3071        .into()
3072}
3073
3074/// Derive `IntoList` for a struct (Rust → R list).
3075///
3076/// - Named structs → named R list: `list(x = 1L, y = 2L)`
3077/// - Tuple structs → unnamed R list: `list(1L, 2L)`
3078/// - Fields annotated `#[into_list(ignore)]` are skipped
3079#[proc_macro_derive(IntoList, attributes(into_list))]
3080pub fn derive_into_list(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3081    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3082    list_derive::derive_into_list(input)
3083        .unwrap_or_else(|e| e.into_compile_error())
3084        .into()
3085}
3086
3087/// Derive `TryFromList` for a struct (R list → Rust).
3088///
3089/// - Named structs: extract by field name. A field whose name appears more
3090///   than once in the list is an error (`SexpError::DuplicateName`), since the
3091///   struct could keep only one of the values; other names, repeated or not,
3092///   are ignored.
3093/// - Tuple structs: extract by position (0, 1, 2, ...)
3094/// - Fields annotated `#[into_list(ignore)]` are not read and are initialized with
3095///   `Default::default()`, so their types must be `Default`
3096#[proc_macro_derive(TryFromList, attributes(into_list))]
3097pub fn derive_try_from_list(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3098    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3099    list_derive::derive_try_from_list(input)
3100        .unwrap_or_else(|e| e.into_compile_error())
3101        .into()
3102}
3103
3104/// Derive `PreferList`: emits an `IntoR` impl selecting list as the type's default
3105/// Rust→R conversion (via `IntoList::into_list`).
3106///
3107/// A type carries exactly one representation default: stacking two `Prefer*`
3108/// derives is a compile error. Each `Prefer*` derive emits a fixed-name marker
3109/// const, so a second one triggers a guided `duplicate definitions with name
3110/// __miniextendr_conflicting_Prefer_derives__keep_ONE_or_use_call_site_As_wrappers`
3111/// error (alongside the raw conflicting-`IntoR`-impl error) — keep one `Prefer*`,
3112/// or drop them all and choose a representation per return value at the call site
3113/// with an `As*` wrapper (`AsList`, `AsExternalPtr`, `AsDataFrame`, ...).
3114///
3115/// # Example
3116///
3117/// ```ignore
3118/// #[derive(IntoList, PreferList)]
3119/// struct Config { verbose: bool, threads: i32 }
3120/// // IntoR produces list(verbose = TRUE, threads = 4L)
3121/// ```
3122#[proc_macro_derive(PreferList)]
3123pub fn derive_prefer_list(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3124    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3125    list_derive::derive_prefer_list(input)
3126        .unwrap_or_else(|e| e.into_compile_error())
3127        .into()
3128}
3129
3130/// Derive `PreferExternalPtr`: return a type with a hand-written `TypedExternal`
3131/// implementation as an R external pointer. `#[derive(ExternalPtr)]` already
3132/// supplies `IntoR`; adding this derive to it creates conflicting implementations.
3133///
3134/// # Example
3135///
3136/// ```ignore
3137/// #[derive(PreferExternalPtr)]
3138/// struct Model { weights: Vec<f64> }
3139///
3140/// impl miniextendr_api::TypedExternal for Model {
3141///     const TYPE_NAME: &'static str = "Model";
3142///     const TYPE_NAME_CSTR: &'static [u8] = b"Model\0";
3143///     const TYPE_ID_CSTR: &'static [u8] = concat!(module_path!(), "::Model\0").as_bytes();
3144/// }
3145/// ```
3146#[proc_macro_derive(PreferExternalPtr)]
3147pub fn derive_prefer_externalptr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3148    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3149    list_derive::derive_prefer_externalptr(input)
3150        .unwrap_or_else(|e| e.into_compile_error())
3151        .into()
3152}
3153
3154/// Derive `PreferRNativeType`: when a newtype wraps an `RNativeType` and also
3155/// implements other conversions, this selects the native R vector conversion
3156/// as the default `IntoR` path.
3157///
3158/// # Example
3159///
3160/// ```ignore
3161/// #[repr(transparent)]
3162/// #[derive(Copy, Clone, RNativeType, PreferRNativeType)]
3163/// struct Meters(f64);
3164/// // IntoR produces a numeric scalar, not an ExternalPtr
3165/// ```
3166#[proc_macro_derive(PreferRNativeType)]
3167pub fn derive_prefer_rnative(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3168    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3169    list_derive::derive_prefer_rnative(input)
3170        .unwrap_or_else(|e| e.into_compile_error())
3171        .into()
3172}
3173
3174/// Derive `PreferVctrs`: emits an `IntoR` impl converting the type to its R vctrs object via
3175/// `IntoVctrs::into_vctrs`.
3176///
3177/// Pair with `#[derive(Vctrs)]` (which supplies `IntoVctrs`) so the type can be returned
3178/// directly from `#[miniextendr]` functions instead of `value.into_vctrs().map_err(...)`.
3179///
3180/// # Example
3181///
3182/// ```ignore
3183/// #[derive(Vctrs, PreferVctrs)]
3184/// #[vctrs(class = "percent", base = "double")]
3185/// struct Percent { #[vctrs(data)] values: Vec<f64> }
3186/// // IntoR builds the `percent` vctrs vector; a build failure becomes an R error.
3187/// ```
3188#[cfg(feature = "vctrs")]
3189#[proc_macro_derive(PreferVctrs)]
3190pub fn derive_prefer_vctrs(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3191    let input = syn::parse_macro_input!(input as syn::DeriveInput);
3192    list_derive::derive_prefer_vctrs(input)
3193        .unwrap_or_else(|e| e.into_compile_error())
3194        .into()
3195}
3196
3197/// Derive `DataFrameRow`: generates a companion `*DataFrame` type with collection fields,
3198/// plus `IntoR` / `TryFromSexp` / `IntoDataFrame` impls for seamless R data.frame conversion.
3199///
3200/// # Example
3201///
3202/// ```ignore
3203/// #[derive(DataFrameRow)]
3204/// struct Measurement {
3205///     time: f64,
3206///     value: f64,
3207/// }
3208///
3209/// // Generates MeasurementDataFrame { time: Vec<f64>, value: Vec<f64> }
3210/// // plus conversion impls
3211/// ```
3212///
3213/// # Struct-level attributes
3214///
3215/// - `#[dataframe(name = "CustomDf")]` — custom name for the generated DataFrame type
3216/// - `#[dataframe(align)]` — pad shorter columns with NA to match longest
3217/// - `#[dataframe(tag = "my_tag")]` — attach a tag attribute to the data.frame
3218/// - `#[dataframe(conflicts = "string")]` — resolve conflicting column types as strings
3219///
3220/// # Field-level attributes
3221///
3222/// - `#[dataframe(skip)]` — omit this field from the DataFrame
3223/// - `#[dataframe(rename = "col")]` — custom column name
3224/// - `#[dataframe(as_list)]` — keep collection as single list column (no expansion)
3225/// - `#[dataframe(expand)]` / `#[dataframe(unnest)]` — expand collection into suffixed columns
3226/// - `#[dataframe(width = N)]` — pin expansion width (shorter rows get NA)
3227///
3228/// # Requirements
3229///
3230/// Of each field type the derive asks only what turning that field into
3231/// columns needs, and a missing impl is reported at the field:
3232///
3233/// - a scalar or opaque column: its column converts to R (`Vec<T>: IntoR`;
3234///   in an enum, `Vec<Option<T>>`);
3235/// - `[T; N]`, or `width` / `expand` over an owned `Vec<T>` / `Box<[T]>`:
3236///   `T`'s columns convert to R; the elements are moved, so `T` needs no
3237///   `Clone` or `Copy`. Over a borrowed `&[T; N]` / `&[T]`, `T` must also be
3238///   `Clone`;
3239/// - a flattened struct or nested enum: `#[derive(DataFrameRow)]`;
3240/// - `as_factor`: `UnitEnumFactor`, which `#[derive(DataFrameRow)]` on a
3241///   unit-only enum and `#[derive(RFactor)]` both provide;
3242/// - `as_list`: `IntoList`;
3243/// - `skip`: nothing.
3244///
3245/// Everything else is generated only where the field types allow it. The
3246/// companion is `Debug` / `Clone` when every column type is. A struct
3247/// companion iterates its rows when every `skip` field's type is `Default`
3248/// (they are rebuilt with `Default::default()`). With `rayon`,
3249/// `from_rows_par` fills the columns in parallel when the rows and column
3250/// values are `Send`, placeholder-filled struct columns are `Default`, and
3251/// flattened / `as_list` values are `Clone`; otherwise it fills them
3252/// sequentially.
3253///
3254/// # Public surface (which verbs to call)
3255///
3256/// Every capability the derive provides has a documented, trait-based (or `std`)
3257/// verb — reach for these, not any incidental inherent plumbing:
3258///
3259/// - **Rows → R `data.frame`**: `rows.into_dataframe()?` (owned, GC-rooted
3260///   `BuiltDataFrame`) or `rows.wrap_data_frame()` (deferred `IntoR` wrapper);
3261///   parallel variant `rows.into_dataframe_par()?`. From the `IntoDataFrame` /
3262///   `AsDataFrameExt` traits (both re-exported from `miniextendr_api::prelude`).
3263/// - **R `data.frame` → rows**: `Vec::<Row>::from_dataframe(&df)?` (parallel:
3264///   `Vec::<Row>::from_dataframe_par(&df)?`), from the `FromDataFrame` trait — or
3265///   the one-call `Row::try_from_dataframe(sexp)` reader on the row type.
3266/// - **Rows ↔ the pure-Rust columnar companion** (`<Row>DataFrame`, `Vec`-columns,
3267///   no R involved): the `ColumnarFrame` trait (in the prelude) —
3268///   `<Row>DataFrame::from_rows(rows)` / `from_rows_par(rows)` (parallel build of
3269///   the *companion*, which `into_dataframe_par` does not give you) and, for
3270///   row-iterable companions, `companion.into_rows()`. `Vec<Row>: Into<companion>`
3271///   and the companion's `IntoIterator` are the equivalent `std` verbs.
3272/// - **Enum split representation**: `rows.into_dataframe_split()` returns one
3273///   `data.frame` per variant (only that variant's columns — no NA fill) as a
3274///   GC-rooted `DataFrameShape` — `Bare` for a single-variant enum,
3275///   `PerVariantList` (an R named list) otherwise — from the
3276///   `IntoDataFrameSplit` trait (in the prelude). Enum rows only; struct
3277///   derives don't partition.
3278///
3279/// The generated `<Row>DataFrame` / `<Row>DataFrameIter` types are intermediate
3280/// column-oriented companions; you rarely name them directly.
3281#[proc_macro_derive(DataFrameRow, attributes(dataframe))]
3282pub fn derive_dataframe_row(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3283    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3284    dataframe_derive::derive_dataframe_row(input)
3285        .unwrap_or_else(|e| e.into_compile_error())
3286        .into()
3287}
3288
3289/// Derive `RFactor`: enables conversion between Rust enums and R factors.
3290///
3291/// # Usage
3292///
3293/// ```ignore
3294/// #[derive(Copy, Clone, RFactor)]
3295/// enum Color {
3296///     Red,
3297///     Green,
3298///     Blue,
3299/// }
3300/// ```
3301///
3302/// # Attributes
3303///
3304/// - `#[r_factor(rename = "name")]` - Rename a variant's level string
3305/// - `#[r_factor(rename_all = "snake_case")]` - Rename all variants (snake_case, kebab-case, lower, upper)
3306///
3307/// # Requirements
3308///
3309/// The enum must be `Copy` (`RFactor` and its supertrait `MatchArg` require it):
3310/// derive `Copy, Clone` with it.
3311#[proc_macro_derive(RFactor, attributes(r_factor))]
3312pub fn derive_r_factor(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3313    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3314    factor_derive::derive_r_factor(input)
3315        .unwrap_or_else(|e| e.into_compile_error())
3316        .into()
3317}
3318
3319/// Derive `MatchArg`: enables conversion between Rust enums and R character strings
3320/// with `match.arg` semantics (partial matching, informative errors).
3321///
3322/// # Usage
3323///
3324/// ```ignore
3325/// #[derive(Copy, Clone, MatchArg)]
3326/// enum Mode {
3327///     Fast,
3328///     Safe,
3329///     Debug,
3330/// }
3331/// ```
3332///
3333/// # Attributes
3334///
3335/// - `#[match_arg(rename = "name")]` - Rename a variant's choice string
3336/// - `#[match_arg(rename_all = "snake_case")]` - Rename all variants (snake_case, kebab-case, lower, upper)
3337/// - `#[match_arg(alias = "name")]` - Another spelling of a variant's choice, matched when typed
3338///   in full; the errors that list the choices name it after them (`(or "name" for "choice")`)
3339/// - `#[match_arg(alias = "name", listed = false)]` - The same, named by no error
3340///
3341/// # Requirements
3342///
3343/// The enum must be `Copy` (`MatchArg: Copy`): derive `Copy, Clone` with it.
3344///
3345/// # Generated Implementations
3346///
3347/// - `MatchArg` - Choice metadata and bidirectional conversion
3348/// - `TryFromSexp` - Convert R STRSXP/factor to enum (with partial matching)
3349/// - `IntoR` - Convert enum to R character scalar
3350#[proc_macro_derive(MatchArg, attributes(match_arg))]
3351pub fn derive_match_arg(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3352    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3353    match_arg_derive::derive_match_arg(input)
3354        .unwrap_or_else(|e| e.into_compile_error())
3355        .into()
3356}
3357
3358/// Derive an R condition type for a struct or enum: `RConditionData` (the R
3359/// class vector, the message and the `data` fields handlers read as
3360/// `e$<name>`) and `From<Self>` for `miniextendr_api::RCondition`.
3361///
3362/// - `#[condition(class = "…")]` on the type sets the family class (default:
3363///   the type name in snake_case); on an enum variant it sets the member class
3364///   (default: `<family>_<variant in snake_case>`). Variants report
3365///   `c(<member>, <family>)`, structs `c(<family>)`.
3366/// - `#[condition(message = "…")]` on a struct or a variant is a `format!`
3367///   string over the fields (tuple fields are `_0`, `_1`, …). Without it the
3368///   message is the type's `Display` rendering.
3369/// - Every field becomes a `data` entry under its own name, converted with
3370///   `RValue::from(field.clone())`, so its type must be `Clone`.
3371///   `#[condition(rename = "…")]`,
3372///   `#[condition(skip)]` and `#[condition(debug)]` (attach the `Debug`
3373///   rendering instead) adjust that; tuple fields need `rename` or `skip`. The
3374///   reserved slots `message`, `call` and `kind` are rejected at compile time.
3375/// - `#[condition(kind = error | warning | message | condition)]` on the type
3376///   or a variant (the variant wins) is the kind `RCondition::from(value)`
3377///   gives it; the default is `error`. Then `.defer()` signals it after the
3378///   function returns its value, and `.raise()` signals it now.
3379///
3380/// The kind is read only through `RCondition::from`. Returned as `Err(value)`
3381/// from a `Result<T, E>`, the value is always an R error, and
3382/// [`defer_warning`](https://docs.rs/miniextendr-api) / `defer_message` signal
3383/// the kind they are named after. Generic types are not supported.
3384#[proc_macro_derive(RCondition, attributes(condition))]
3385pub fn derive_r_condition(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3386    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3387    condition_derive::derive_r_condition(input)
3388        .unwrap_or_else(|e| e.into_compile_error())
3389        .into()
3390}
3391
3392/// Derive `TryFromSexp` for a single-field newtype: forward the R → Rust
3393/// conversion to the inner type.
3394///
3395/// Generates a scalar `TryFromSexp` impl that delegates to the inner type (so the
3396/// newtype inherits its exact SEXPTYPE checks, NA policy, and error text), plus a
3397/// `TryFromSexpElement` marker impl. The marker lets `miniextendr-api`'s container
3398/// blankets light up `Vec<T>` / `Option<T>` / `Vec<Option<T>>` automatically,
3399/// each one where the inner type's container converts.
3400///
3401/// Don't use it on a type that also gets `try_from_sexp_via_str_parse!`: both
3402/// implement `TryFromSexpElement`.
3403///
3404/// # Usage
3405///
3406/// ```ignore
3407/// use uuid::Uuid;
3408///
3409/// #[derive(TryFromSexp)]            // R -> Rust only
3410/// struct Pattern(regex::Regex);
3411///
3412/// #[derive(TryFromSexp, IntoR)]     // round-trip; Vec/Option containers work too
3413/// struct UserId(Uuid);
3414/// ```
3415///
3416/// Direction is chosen by which derive you list — derive only `TryFromSexp` for
3417/// inner types that read from R but cannot be written back (e.g. `regex::Regex`).
3418///
3419/// # Refusing values: `#[try_from_sexp(validate = path)]`
3420///
3421/// The containers read the inner type and wrap it; they never run the
3422/// newtype's own `try_from_sexp`. To refuse a value the inner type accepts,
3423/// name a check, `fn(SEXP) -> Result<(), E>` with `E: Into<SexpError>`. It
3424/// becomes `TryFromSexpElement::check_sexp`, which runs on the R value before
3425/// it is read, in every shape: the scalar, on any input but `NULL` for
3426/// `Option<T>`, and for `Vec<T>` and `Vec<Option<T>>` once on the whole vector
3427/// of an atomic inner type, or on each element (but `NULL`) of a list when the
3428/// inner type is `List` (a list of R objects, each with its own class).
3429///
3430/// ```ignore
3431/// use miniextendr_api::condition::RError;
3432/// use miniextendr_api::{SEXP, SexpExt, TryFromSexp};
3433///
3434/// fn plain_number(x: SEXP) -> Result<(), RError> {
3435///     if x.inherits_class(c"difftime") {
3436///         return Err(RError::new("give the duration as a plain number").class("pkg_unit_error"));
3437///     }
3438///     Ok(())
3439/// }
3440///
3441/// #[derive(TryFromSexp)]
3442/// #[try_from_sexp(validate = plain_number)]
3443/// struct Duration(f64);
3444/// ```
3445///
3446/// An `RError` refusal (or one of any `RConditionData` type) keeps its classes
3447/// and fields on the argument error, before the crate's
3448/// `conversion_error_class`, with `e$param` and the message
3449/// `'<p>' must be <expected>: <message>`. With `validate`, the newtype's
3450/// scalar error is `SexpError`, so the inner type's error must convert into it,
3451/// as every built-in conversion error and every `RConditionData` type does.
3452/// `validate` is the only key, and the attribute goes on the struct.
3453#[proc_macro_derive(TryFromSexp, attributes(try_from_sexp))]
3454pub fn derive_try_from_sexp(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3455    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3456    newtype_derive::derive_try_from_sexp(input)
3457        .unwrap_or_else(|e| e.into_compile_error())
3458        .into()
3459}
3460
3461/// Derive `IntoR` for a single-field newtype: forward the Rust → R conversion to
3462/// the inner type.
3463///
3464/// Generates a scalar `IntoR` impl that delegates to the inner type, plus an
3465/// `IntoRNewtype` marker (powering the `Option<T>` / `Vec<Option<T>>` container
3466/// blankets) and a concrete `IntoRVecElement` impl (powering `Vec<T>`). See
3467/// `#[derive(TryFromSexp)]` for usage.
3468///
3469/// Do not derive both `IntoR` and `MatchArg` on the same type: both feed the
3470/// single `IntoR for Vec<T>` blanket slot and would collide (E0119).
3471#[proc_macro_derive(IntoR)]
3472pub fn derive_into_r(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3473    let input = match ::syn::parse::<syn::DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::DeriveInput);
3474    newtype_derive::derive_into_r(input)
3475        .unwrap_or_else(|e| e.into_compile_error())
3476        .into()
3477}
3478
3479/// Derive `Vctrs`: enables creating vctrs-compatible S3 vector classes from Rust structs.
3480///
3481/// # Usage
3482///
3483/// ```ignore
3484/// #[derive(Vctrs)]
3485/// #[vctrs(class = "percent", base = "double")]
3486/// pub struct Percent {
3487///     data: Vec<f64>,
3488/// }
3489/// ```
3490///
3491/// # Attributes
3492///
3493/// - `#[vctrs(class = "name")]` - R class name (required)
3494/// - `#[vctrs(base = "type")]` - Base type: double, integer, logical, character, raw, list, record
3495/// - `#[vctrs(abbr = "abbr")]` - Abbreviation for `vec_ptype_abbr`
3496/// - `#[vctrs(inherit_base = true|false)]` - Whether to include base type in class vector
3497///
3498/// # Generated Implementations
3499///
3500/// - `VctrsClass` - Metadata trait for vctrs class information
3501/// - `VctrsRecord` (for `base = "record"`) - Field names for record types
3502#[cfg(feature = "vctrs")]
3503#[proc_macro_derive(Vctrs, attributes(vctrs))]
3504pub fn derive_vctrs(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3505    let input = syn::parse_macro_input!(input as syn::DeriveInput);
3506    vctrs_derive::derive_vctrs(input)
3507        .unwrap_or_else(|e| e.into_compile_error())
3508        .into()
3509}
3510
3511/// Create a `TypedListSpec` for validating `...` arguments or lists.
3512///
3513/// This macro provides ergonomic syntax for defining typed list specifications
3514/// that can be used with `Dots::typed()` to validate the structure of
3515/// `...` arguments passed from R.
3516///
3517/// # Syntax
3518///
3519/// ```text
3520/// typed_list!(
3521///     name => type_spec,    // required field with type
3522///     name? => type_spec,   // optional field with type
3523///     name,                 // required field, any type
3524///     name?,                // optional field, any type
3525/// )
3526/// ```
3527///
3528/// For strict mode (no extra fields allowed):
3529/// ```text
3530/// typed_list!(@exact; name => type_spec, ...)
3531/// ```
3532///
3533/// # Type Specifications
3534///
3535/// ## Base types (with optional length)
3536/// - `numeric()` / `numeric(4)` - Real/double vector
3537/// - `integer()` / `integer(4)` - Integer vector
3538/// - `logical()` / `logical(4)` - Logical vector
3539/// - `character()` / `character(4)` - Character vector
3540/// - `raw()` / `raw(4)` - Raw vector
3541/// - `complex()` / `complex(4)` - Complex vector
3542/// - `list()` / `list(4)` - List (VECSXP)
3543///
3544/// ## Special types
3545/// - `data_frame()` - Data frame
3546/// - `factor()` - Factor
3547/// - `matrix()` - Matrix
3548/// - `array()` - Array
3549/// - `function()` - Function
3550/// - `environment()` - Environment
3551/// - `null()` - NULL only
3552/// - `any()` - Any type
3553///
3554/// ## String literals
3555/// - `"numeric"`, `"integer"`, etc. - Same as call syntax
3556/// - `"data.frame"` - Data frame (alias)
3557/// - `"MyClass"` - Any other string is treated as a class name (uses `Rf_inherits`)
3558///
3559/// # Examples
3560///
3561/// ## Basic usage
3562///
3563/// ```ignore
3564/// use miniextendr_api::{miniextendr, typed_list, Dots};
3565///
3566/// #[miniextendr]
3567/// pub fn process_args(dots: ...) -> Result<i32, String> {
3568///     let args = dots.typed(typed_list!(
3569///         alpha => numeric(4),
3570///         beta => list(),
3571///         gamma? => "character",
3572///     )).map_err(|e| e.to_string())?;
3573///
3574///     let alpha: Vec<f64> = args.get("alpha").map_err(|e| e.to_string())?;
3575///     Ok(alpha.len() as i32)
3576/// }
3577/// ```
3578///
3579/// ## Strict mode
3580///
3581/// ```ignore
3582/// // Reject any extra named fields
3583/// let args = dots.typed(typed_list!(@exact;
3584///     x => numeric(),
3585///     y => numeric(),
3586/// ))?;
3587/// ```
3588///
3589/// ## Class checking
3590///
3591/// ```ignore
3592/// // Check for specific R class (uses Rf_inherits semantics)
3593/// let args = dots.typed(typed_list!(
3594///     data => "data.frame",
3595///     model => "lm",
3596/// ))?;
3597/// ```
3598///
3599/// ## Attribute sugar
3600///
3601/// Instead of calling `.typed()` manually, you can use `typed_list!` directly in the
3602/// `#[miniextendr]` attribute for automatic validation:
3603///
3604/// ```ignore
3605/// #[miniextendr(dots = typed_list!(x => numeric(), y => numeric()))]
3606/// pub fn my_func(_: ...) -> String {
3607///     // `dots_typed` is automatically created and validated
3608///     let x: f64 = dots_typed.get("x").expect("x");
3609///     let y: f64 = dots_typed.get("y").expect("y");
3610///     format!("x={}, y={}", x, y)
3611/// }
3612/// ```
3613///
3614/// This injects validation at the start of the function body, reading the
3615/// dots binding (`__miniextendr_dots` for `_: ...`, otherwise the name of the
3616/// `name: ...` / `name: &Dots` parameter):
3617/// ```ignore
3618/// let dots_typed = __miniextendr_dots.typed(typed_list!(...))
3619///     .unwrap_or_else(|e| miniextendr_api::arg_error!(
3620///         param = "...", "dots validation failed: {e}"
3621///     ));
3622/// ```
3623///
3624/// See the [`#[miniextendr]`](macro@miniextendr) attribute documentation for more details.
3625///
3626#[proc_macro]
3627pub fn typed_list(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3628    let parsed = match ::syn::parse::<typed_list::TypedListInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as typed_list::TypedListInput);
3629    typed_list::expand_typed_list(parsed).into()
3630}
3631
3632/// Define a compile-time-validated wrapper for an R `data.frame` input.
3633///
3634/// `typed_dataframe!` mirrors [`typed_list!`] for the data.frame shape:
3635/// declare the columns once, get a struct that implements `TryFromSexp`
3636/// (validating both the `data.frame` class and per-column SEXPTYPE) plus
3637/// per-column borrowed accessors that return `&[T]`.
3638///
3639/// # Syntax
3640///
3641/// ```ignore
3642/// typed_dataframe! {
3643///     /// The shape we accept for the Theoph PK dataset.
3644///     pub TheophDf {
3645///         subject: i32,
3646///         weight: f64,
3647///         dose: f64,
3648///         flag: Option<i32>,   // optional column
3649///     }
3650/// }
3651/// ```
3652///
3653/// For strict mode (reject any column not declared):
3654/// ```ignore
3655/// typed_dataframe! {
3656///     @exact;
3657///     pub Strict { x: i32 }
3658/// }
3659/// ```
3660///
3661/// # Supported element types
3662///
3663/// v1 supports column element types that implement
3664/// `miniextendr_api::RNativeType`:
3665///
3666/// - `i32` — `INTSXP`
3667/// - `f64` — `REALSXP`
3668/// - `u8` — `RAWSXP`
3669/// - `miniextendr_api::RLogical` — `LGLSXP`
3670/// - `miniextendr_api::Rcomplex` — `CPLXSXP`
3671///
3672/// `String`/`&str` column types are not yet supported (character vectors
3673/// don't expose a contiguous slice). `bool` is also not yet supported as
3674/// a direct field type — use `RLogical` and convert per-element, or
3675/// follow the open follow-up issues from PR #698.
3676///
3677/// # Generated API
3678///
3679/// For each `name: T` column the macro emits:
3680/// - `pub fn name(&self) -> &[T]` (required)
3681/// - `pub fn name(&self) -> Option<&[T]>` (optional, `Option<T>`)
3682///
3683/// Plus housekeeping:
3684/// - `pub fn nrow(&self) -> usize`
3685/// - `pub fn ncol(&self) -> usize` (count of *declared* columns)
3686/// - `pub fn as_sexp(&self) -> SEXP`
3687///
3688/// All borrowed accessors are bound to `&self`; the SEXP is protected
3689/// by the surrounding `#[miniextendr]` call wrapper while the struct is
3690/// alive.
3691///
3692/// # Error reporting
3693///
3694/// `TryFromSexp::try_from_sexp` batches every per-column error into a
3695/// single `SexpError::InvalidValue`, so the R user sees one diagnostic
3696/// covering all missing or wrong-typed columns rather than a sequence of
3697/// stop-on-first-failure messages.
3698///
3699/// # Example
3700///
3701/// ```ignore
3702/// use miniextendr_api::{miniextendr, typed_dataframe};
3703///
3704/// typed_dataframe! {
3705///     pub TheophDf {
3706///         subject: i32,
3707///         weight: f64,
3708///         dose: f64,
3709///     }
3710/// }
3711///
3712/// #[miniextendr]
3713/// pub fn theoph_nrow(df: TheophDf) -> i32 {
3714///     // df.subject() -> &[i32], df.weight() -> &[f64]
3715///     // Lengths are guaranteed equal across columns (data.frame invariant).
3716///     df.nrow() as i32
3717/// }
3718/// ```
3719///
3720/// [`typed_list!`]: macro@typed_list
3721#[proc_macro]
3722pub fn typed_dataframe(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3723    let parsed = match ::syn::parse::<typed_dataframe::TypedDataframeInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as typed_dataframe::TypedDataframeInput);
3724    typed_dataframe::expand_typed_dataframe(parsed).into()
3725}
3726
3727/// Construct an R list from Rust values.
3728///
3729/// This macro provides a convenient way to create R lists in Rust code,
3730/// using R-like syntax. Values are converted to R objects via the [`IntoR`] trait.
3731///
3732/// # Syntax
3733///
3734/// ```ignore
3735/// // Named entries (like R's list())
3736/// list!(
3737///     alpha = 1,
3738///     beta = "hello",
3739///     "my-name" = vec![1, 2, 3],
3740/// )
3741///
3742/// // Unnamed entries
3743/// list!(1, "hello", vec![1, 2, 3])
3744///
3745/// // Mixed (unnamed entries get empty string names)
3746/// list!(alpha = 1, 2, beta = "hello")
3747///
3748/// // Empty list
3749/// list!()
3750/// ```
3751///
3752/// # Examples
3753///
3754/// ```ignore
3755/// use miniextendr_api::{list, IntoR};
3756///
3757/// // Create a named list
3758/// let my_list = list!(
3759///     x = 42,
3760///     y = "hello world",
3761///     z = vec![1.0, 2.0, 3.0],
3762/// );
3763///
3764/// // In R this is equivalent to:
3765/// // list(x = 42L, y = "hello world", z = c(1, 2, 3))
3766/// ```
3767///
3768/// [`IntoR`]: https://docs.rs/miniextendr-api/latest/miniextendr_api/into_r/trait.IntoR.html
3769#[proc_macro]
3770pub fn list(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3771    let parsed = match ::syn::parse::<list_macro::ListInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as list_macro::ListInput);
3772    list_macro::expand_list(parsed).into()
3773}
3774
3775/// Evaluate R code written as **Rust tokens**, validated at compile time.
3776///
3777/// `r!` takes a single R expression as a token stream, `stringify!`s it into a
3778/// static R source string at build time, and evaluates it via
3779/// `miniextendr_api::expression::r_eval_str` (the same protect-safe parse + eval
3780/// path as `r_str!`).
3781///
3782/// # What you get today
3783///
3784/// Because the argument is a Rust token tree, the Rust front-end already
3785/// rejects **unbalanced delimiters** (`r!(f(1, 2)` won't compile) and
3786/// lexically invalid tokens before R ever sees the string — a cheap
3787/// compile-time guard over the pure-runtime `r_str!`. The source is lowered to
3788/// a `&'static str` (`stringify!`), so there is no `format!` allocation at the
3789/// call site.
3790///
3791/// This proc-macro additionally validates a conservative subset of known-bad
3792/// R syntax constructs (trailing binary operators, consecutive non-unary
3793/// binary operators, bare `if`/`while`/`for` without a body, etc.) and emits
3794/// a precise compile error pointing at the offending token. Empty (missing)
3795/// call arguments — `f(, x)`, `matrix(, 2, 2)` — are valid R and pass.
3796///
3797/// # What is deferred
3798///
3799/// Direct `Rf_lang*` call-tree lowering (skipping the runtime parser entirely)
3800/// is tracked as a follow-up in issue #938 (item 2). Until then `r!` parses
3801/// its static string at first evaluation, exactly like `r_str!`.
3802///
3803/// # Non-goals
3804///
3805/// A complete R grammar validator is not achievable over Rust tokens:
3806/// - Single-quoted strings (`'hello'`) and backtick-quoted names (`` `foo` ``)
3807///   already die at the Rust lexer — nothing to validate.
3808/// - `%op%` tokenises as `%`, ident, `%` and is accepted without analysis.
3809/// - Anything the validator cannot confidently classify as wrong passes through
3810///   unvalidated (conservative reject-only-known-bad design).
3811///
3812/// # Forms
3813///
3814/// - `r!(R tokens…)` — evaluate in `R_GlobalEnv`.
3815/// - `r!(env: e; R tokens…)` — evaluate in the environment SEXP `e`. The
3816///   leading `env: <expr> ;` is consumed as Rust, the rest is R source.
3817///
3818/// Both evaluate to `Result<SEXP, REvalError>`; the `SEXP` is **unprotected**.
3819///
3820/// # Safety
3821///
3822/// Expands to an `unsafe` block; the underlying FFI is `#[r_ffi_checked]`, so
3823/// calls from a worker thread are serialized onto the R thread.
3824///
3825/// # Example
3826///
3827/// ```ignore
3828/// let three = r!(1L + 2L)?;
3829/// let rows = r!(getFromNamespace(".theoph_rows", "dataframeflows")())?;
3830/// let in_env = r!(env: my_env; x + 1)?;
3831/// ```
3832#[proc_macro]
3833pub fn r(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3834    r_macro::expand(input)
3835}
3836
3837/// Re-export other packages' R functions from this package.
3838///
3839/// Each item names a package and one of its exports. The generated R
3840/// wrappers get roxygen2's re-export block for it, so `devtools::document()`
3841/// writes `importFrom(pkg, name)` and `export(name)` to `NAMESPACE` and lists
3842/// `name` on `man/reexports.Rd`:
3843///
3844/// ```r
3845/// #' @importFrom generics tidy
3846/// #' @export
3847/// generics::tidy
3848/// ```
3849///
3850/// The usual reason is a generic the package registers S3 methods for, such
3851/// as `generics::tidy()` for `#[miniextendr(s3(generic = "tidy", class =
3852/// "my_fit"))]`: with only the package attached, a bare `tidy(fit)` then finds
3853/// the generic and dispatches to the method.
3854///
3855/// ```ignore
3856/// use miniextendr_api::reexport;
3857///
3858/// reexport!(generics::tidy, generics::glance, generics::augment);
3859/// reexport!(data.table::fread, stats::na.omit, magrittr::"%>%");
3860/// ```
3861///
3862/// - The package and the name are identifiers joined by `.`. A name that is
3863///   not a syntactic R name is a string literal (`magrittr::"%>%"`); the R
3864///   code backticks it.
3865/// - The package has to be in the `Imports` field of `DESCRIPTION`, which the
3866///   `importFrom()` needs; `R CMD check` reports a missing one.
3867/// - Any number of invocations may name the same `pkg::name`, so each module
3868///   can re-export the generics its methods are for; the wrappers get one
3869///   block per `pkg::name`.
3870/// - The wrapper writer refuses, at install time, a name re-exported from two
3871///   packages, and a name the package's generated wrappers also define or
3872///   export (an S3 generic of an impl block, a function, an
3873///   `@rawNamespace export(...)`). Hand-written R files are not checked.
3874/// - A package name that is not an R package name, a name that is not an R
3875///   name, and two packages for one name in one invocation are compile
3876///   errors, all reported at once.
3877#[proc_macro]
3878pub fn reexport(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3879    reexport::expand(input.into()).into()
3880}
3881
3882/// Internal proc macro used by TPIE (Trait-Provided Impl Expansion).
3883///
3884/// Called by the metadata macro re-exported alongside each `#[miniextendr]` trait.
3885/// Do not call directly.
3886#[proc_macro]
3887#[doc(hidden)]
3888pub fn __mx_trait_impl_expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3889    miniextendr_impl_trait::expand_tpie(input)
3890}
3891
3892/// Generate `TypedExternal` and `IntoExternalPtr` impls for a concrete monomorphization
3893/// of a generic type.
3894///
3895/// Since `#[derive(ExternalPtr)]` rejects generic types, use this macro to generate
3896/// the necessary impls for a specific type instantiation.
3897///
3898/// # Example
3899///
3900/// ```ignore
3901/// struct Wrapper<T> { inner: T }
3902///
3903/// impl_typed_external!(Wrapper<i32>);
3904/// impl_typed_external!(Wrapper<String>);
3905/// ```
3906#[proc_macro]
3907pub fn impl_typed_external(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3908    match typed_external_macro::impl_typed_external(input.into()) {
3909        Ok(tokens) => tokens.into(),
3910        Err(err) => err.into_compile_error().into(),
3911    }
3912}
3913
3914/// Generate the `R_init_*` entry point for a miniextendr R package.
3915///
3916/// This macro consolidates all package initialization into a single line.
3917/// It generates an `extern "C-unwind"` function that R calls when loading
3918/// the shared library.
3919///
3920/// # Usage
3921///
3922/// ```ignore
3923/// // Auto-detects package name from CARGO_CRATE_NAME (recommended):
3924/// miniextendr_api::miniextendr_init!();
3925///
3926/// // Or specify explicitly (for edge cases):
3927/// miniextendr_api::miniextendr_init!(mypkg);
3928/// ```
3929///
3930/// The generated function calls `miniextendr_api::init::package_init` which
3931/// handles panic hooks, runtime init, locale assertion, ALTREP setup, trait ABI
3932/// registration, routine registration, and symbol locking.
3933///
3934/// Call it at the crate root: it also defines the crate's marker type, under
3935/// which `serde_error_shape!` declarations are keyed and which, with the API's
3936/// `serde` feature, every `#[miniextendr]` function or method returning a
3937/// `Result` names.
3938#[proc_macro]
3939pub fn miniextendr_init(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3940    let pkg_name: syn::Ident = if input.is_empty() {
3941        // Auto-detect from CARGO_CRATE_NAME (set by cargo during compilation).
3942        // Cargo normalizes hyphens → underscores, so this is almost always a
3943        // valid Rust/C identifier. Still parse through syn so malformed values
3944        // surface as a compile error rather than an ICE.
3945        let name = match std::env::var("CARGO_CRATE_NAME") {
3946            Ok(n) => n,
3947            Err(_) => {
3948                return syn::Error::new(
3949                    proc_macro2::Span::call_site(),
3950                    "CARGO_CRATE_NAME not set. Either pass the package name explicitly: \
3951                     miniextendr_init!(mypkg), or ensure you're building with cargo.",
3952                )
3953                .into_compile_error()
3954                .into();
3955            }
3956        };
3957        match syn::parse_str::<syn::Ident>(&name) {
3958            Ok(id) => id,
3959            Err(_) => {
3960                return syn::Error::new(
3961                    proc_macro2::Span::call_site(),
3962                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("CARGO_CRATE_NAME `{0}` is not a valid C identifier; R_init_<pkg> must match `[A-Za-z_][A-Za-z0-9_]*`. Pass the name explicitly: miniextendr_init!(my_pkg).",
                name))
    })format!(
3963                        "CARGO_CRATE_NAME `{name}` is not a valid C identifier; \
3964                         R_init_<pkg> must match `[A-Za-z_][A-Za-z0-9_]*`. \
3965                         Pass the name explicitly: miniextendr_init!(my_pkg)."
3966                    ),
3967                )
3968                .into_compile_error()
3969                .into();
3970            }
3971        }
3972    } else {
3973        match ::syn::parse::<syn::Ident>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as syn::Ident)
3974    };
3975    let fn_name = syn::Ident::new(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("R_init_{0}", pkg_name))
    })format!("R_init_{}", pkg_name), pkg_name.span());
3976    let unload_name = syn::Ident::new(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("R_unload_{0}", pkg_name))
    })format!("R_unload_{}", pkg_name), pkg_name.span());
3977
3978    // Build a byte string literal with NUL terminator for the package name.
3979    let mut name_bytes = pkg_name.to_string().into_bytes();
3980    name_bytes.push(0);
3981    let byte_lit = syn::LitByteStr::new(&name_bytes, pkg_name.span());
3982
3983    // The crate's `conversion_error_class`, for the wrappers preamble's
3984    // `.miniextendr_conversion_error_class`: the R-side argument checks raise
3985    // with the classes the conversion arms of this crate carry (#1591).
3986    let conversion_error_class = crate::crate_config::conversion_error_class();
3987    let init_marker = crate::miniextendr_fn::init_marker_ident();
3988
3989    let expanded = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" The crate\'s marker type. A `serde_error_shape!` declaration is an\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" impl keyed by it, and the generated `Err` arms look the declaration\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" up under it, so the macro must be called at the crate root.\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "hidden");
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "pub");
    ::quote::__private::push_ident(&mut _s, "enum");
    ::quote::ToTokens::to_tokens(&init_marker, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        ::quote::__private::TokenStream::new());
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" The package crate\'s `conversion_error_class`, read by the\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" wrapper-generation pass and the raising guards (host-only; the\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" `wasm_registry.rs` snapshot carries it to wasm32).\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "cfg");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "not");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "target_arch");
                            ::quote::__private::push_eq(&mut _s);
                            ::quote::__private::parse(&mut _s, "\"wasm32\"");
                            _s
                        });
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "linkme");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "distributed_slice");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "registry");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s,
                        "MX_CONVERSION_ERROR_CLASS");
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "linkme");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "crate");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "linkme");
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "hidden");
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "static");
    ::quote::__private::push_ident(&mut _s, "__MX_CONVERSION_ERROR_CLASS");
    ::quote::__private::push_colon(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "registry");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ConversionErrorClassEntry");
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "registry");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ConversionErrorClassEntry");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "classes");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    {
                        use ::quote::__private::ext::*;
                        let mut _first = true;
                        let has_iter = ::quote::__private::HasIterator::<false>;
                        #[allow(unused_mut)]
                        let (mut conversion_error_class, i) =
                            conversion_error_class.quote_into_iter();
                        let has_iter = has_iter | i;
                        <_ as
                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                        while true {
                            let conversion_error_class =
                                match conversion_error_class.next() {
                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                    None => break,
                                };
                            if !_first { ::quote::__private::push_comma(&mut _s); }
                            _first = false;
                            ::quote::ToTokens::to_tokens(&conversion_error_class,
                                &mut _s);
                        }
                    }
                    _s
                });
            ::quote::__private::push_comma(&mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "cfg");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "target_arch");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::parse(&mut _s, "\"wasm32\"");
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "path");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s, "\"wasm_registry.rs\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "hidden");
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "mod");
    ::quote::__private::push_ident(&mut _s, "__miniextendr_wasm_registry");
    ::quote::__private::push_semi(&mut _s);
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "unsafe");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "no_mangle");
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "pub");
    ::quote::__private::push_ident(&mut _s, "unsafe");
    ::quote::__private::push_ident(&mut _s, "extern");
    ::quote::__private::parse(&mut _s, "\"C-unwind\"");
    ::quote::__private::push_ident(&mut _s, "fn");
    ::quote::ToTokens::to_tokens(&fn_name, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "dll");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_star(&mut _s);
            ::quote::__private::push_ident(&mut _s, "mut");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "sys");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "DllInfo");
            ::quote::__private::push_comma(&mut _s);
            _s
        });
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "let");
            ::quote::__private::push_ident(&mut _s,
                "__miniextendr_init_result");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "std");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "panic");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "catch_unwind");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "std");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "panic");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "AssertUnwindSafe");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_or_or(&mut _s);
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Brace,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_pound(&mut _s);
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Bracket,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s, "cfg");
                                            ::quote::__private::push_group(&mut _s,
                                                ::quote::__private::Delimiter::Parenthesis,
                                                {
                                                    let mut _s = ::quote::__private::TokenStream::new();
                                                    ::quote::__private::push_ident(&mut _s, "target_arch");
                                                    ::quote::__private::push_eq(&mut _s);
                                                    ::quote::__private::parse(&mut _s, "\"wasm32\"");
                                                    _s
                                                });
                                            _s
                                        });
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "registry");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s,
                                        "install_wasm_runtime_slices");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s,
                                                "__miniextendr_wasm_registry");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s,
                                                "MX_CALL_DEFS_WASM");
                                            ::quote::__private::push_comma(&mut _s);
                                            ::quote::__private::push_ident(&mut _s,
                                                "__miniextendr_wasm_registry");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s,
                                                "MX_ALTREP_REGISTRATIONS_WASM");
                                            ::quote::__private::push_comma(&mut _s);
                                            ::quote::__private::push_ident(&mut _s,
                                                "__miniextendr_wasm_registry");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s,
                                                "MX_TRAIT_DISPATCH_WASM");
                                            ::quote::__private::push_comma(&mut _s);
                                            ::quote::__private::push_ident(&mut _s,
                                                "__miniextendr_wasm_registry");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s,
                                                "MX_CONVERSION_ERROR_CLASS_WASM");
                                            ::quote::__private::push_comma(&mut _s);
                                            _s
                                        });
                                    ::quote::__private::push_semi(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "unsafe");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Brace,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s, "let");
                                            ::quote::__private::push_ident(&mut _s, "pkg_name");
                                            ::quote::__private::push_eq(&mut _s);
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "std");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "ffi");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "CStr");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s,
                                                "from_bytes_with_nul_unchecked");
                                            ::quote::__private::push_group(&mut _s,
                                                ::quote::__private::Delimiter::Parenthesis,
                                                {
                                                    let mut _s = ::quote::__private::TokenStream::new();
                                                    ::quote::ToTokens::to_tokens(&byte_lit, &mut _s);
                                                    _s
                                                });
                                            ::quote::__private::push_semi(&mut _s);
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "init");
                                            ::quote::__private::push_colon2(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "package_init");
                                            ::quote::__private::push_group(&mut _s,
                                                ::quote::__private::Delimiter::Parenthesis,
                                                {
                                                    let mut _s = ::quote::__private::TokenStream::new();
                                                    ::quote::__private::push_ident(&mut _s, "dll");
                                                    ::quote::__private::push_comma(&mut _s);
                                                    ::quote::__private::push_ident(&mut _s, "pkg_name");
                                                    _s
                                                });
                                            ::quote::__private::push_semi(&mut _s);
                                            _s
                                        });
                                    _s
                                });
                            _s
                        });
                    ::quote::__private::push_comma(&mut _s);
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "if");
            ::quote::__private::push_ident(&mut _s, "let");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "result");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Err");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "payload");
                    _s
                });
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_ident(&mut _s,
                "__miniextendr_init_result");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "mem");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "drop");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "payload");
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "unsafe");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "sys");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s,
                                "Rf_error_unchecked");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::parse(&mut _s,
                                        "c\"miniextendr package initialization panicked before completing\"");
                                    ::quote::__private::push_dot(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "as_ptr");
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        ::quote::__private::TokenStream::new());
                                    ::quote::__private::push_comma(&mut _s);
                                    _s
                                });
                            ::quote::__private::push_semi(&mut _s);
                            _s
                        });
                    _s
                });
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" R_unload_<pkg> entry point \u{2014} R calls this on `detach(unload=TRUE)` /\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" `dyn.unload()`. Signals the miniextendr worker thread (if enabled)\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" to exit cleanly. See `#103`.\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "unsafe");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "no_mangle");
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "pub");
    ::quote::__private::push_ident(&mut _s, "unsafe");
    ::quote::__private::push_ident(&mut _s, "extern");
    ::quote::__private::parse(&mut _s, "\"C-unwind\"");
    ::quote::__private::push_ident(&mut _s, "fn");
    ::quote::ToTokens::to_tokens(&unload_name, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "_dll");
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_star(&mut _s);
            ::quote::__private::push_ident(&mut _s, "mut");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "sys");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "DllInfo");
            ::quote::__private::push_comma(&mut _s);
            _s
        });
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "worker");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s,
                "miniextendr_runtime_shutdown");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                ::quote::__private::TokenStream::new());
            ::quote::__private::push_semi(&mut _s);
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" Linker anchor: stub.c takes the address of this symbol to force the\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" linker to pull in the user crate\'s archive member from the staticlib.\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" With codegen-units = 1, this single member contains all linkme\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" distributed_slice entries. The name is package-independent so stub.c\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" doesn\'t need configure substitution.\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s, "r\"\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" Defined as a function rather than a static so it stays exported under\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" the webR wasm RUSTFLAG -Zdefault-visibility=hidden, which keeps\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" no_mangle functions exported (like the R_init entry point) but hides\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" no_mangle statics. A hidden anchor breaks wasm side-module dlopen\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "doc");
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::parse(&mut _s,
                "r\" (bad export type, undefined). See miniextendr webR notes (#494).\"");
            _s
        });
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "unsafe");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "no_mangle");
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "pub");
    ::quote::__private::push_ident(&mut _s, "extern");
    ::quote::__private::parse(&mut _s, "\"C\"");
    ::quote::__private::push_ident(&mut _s, "fn");
    ::quote::__private::push_ident(&mut _s, "miniextendr_force_link");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        ::quote::__private::TokenStream::new());
    _s
}quote::quote! {
3990        /// The crate's marker type. A `serde_error_shape!` declaration is an
3991        /// impl keyed by it, and the generated `Err` arms look the declaration
3992        /// up under it, so the macro must be called at the crate root.
3993        #[doc(hidden)]
3994        pub enum #init_marker {}
3995
3996        /// The package crate's `conversion_error_class`, read by the
3997        /// wrapper-generation pass and the raising guards (host-only; the
3998        /// `wasm_registry.rs` snapshot carries it to wasm32).
3999        #[cfg(not(target_arch = "wasm32"))]
4000        #[::miniextendr_api::linkme::distributed_slice(::miniextendr_api::registry::MX_CONVERSION_ERROR_CLASS)]
4001        #[linkme(crate = ::miniextendr_api::linkme)]
4002        #[doc(hidden)]
4003        static __MX_CONVERSION_ERROR_CLASS: ::miniextendr_api::registry::ConversionErrorClassEntry =
4004            ::miniextendr_api::registry::ConversionErrorClassEntry {
4005                classes: &[#(#conversion_error_class),*],
4006            };
4007
4008        // wasm32: pull in the host-generated `wasm_registry.rs` snapshot
4009        // (committed by the user crate, regenerated by `just wasm-prepare`).
4010        // The path is relative to the file invoking `miniextendr_init!()` —
4011        // by convention `<crate>/src/rust/lib.rs`, so the snapshot sits at
4012        // `<crate>/src/rust/wasm_registry.rs`. Module is `#[doc(hidden)]`
4013        // because it's purely an internal bridge between the user crate's
4014        // wrapper / vtable / register-fn `#[no_mangle]` exports and
4015        // `miniextendr_api::registry::install_wasm_runtime_slices`.
4016        #[cfg(target_arch = "wasm32")]
4017        #[path = "wasm_registry.rs"]
4018        #[doc(hidden)]
4019        mod __miniextendr_wasm_registry;
4020
4021        #[unsafe(no_mangle)]
4022        pub unsafe extern "C-unwind" fn #fn_name(
4023            dll: *mut ::miniextendr_api::sys::DllInfo,
4024        ) {
4025            let __miniextendr_init_result = ::std::panic::catch_unwind(
4026                ::std::panic::AssertUnwindSafe(|| {
4027                    // wasm32: install the pre-generated runtime tables before
4028                    // package_init runs. linkme didn't gather anything (the slices
4029                    // are OnceLock-backed on wasm32), so register_routines /
4030                    // universal_query would otherwise see empty slices, and the
4031                    // raising guards no crate conversion classes (#1768).
4032                    #[cfg(target_arch = "wasm32")]
4033                    ::miniextendr_api::registry::install_wasm_runtime_slices(
4034                        __miniextendr_wasm_registry::MX_CALL_DEFS_WASM,
4035                        __miniextendr_wasm_registry::MX_ALTREP_REGISTRATIONS_WASM,
4036                        __miniextendr_wasm_registry::MX_TRAIT_DISPATCH_WASM,
4037                        __miniextendr_wasm_registry::MX_CONVERSION_ERROR_CLASS_WASM,
4038                    );
4039
4040                    unsafe {
4041                        // SAFETY: byte literal is a valid NUL-terminated string produced by the macro.
4042                        let pkg_name = ::std::ffi::CStr::from_bytes_with_nul_unchecked(#byte_lit);
4043                        ::miniextendr_api::init::package_init(dll, pkg_name);
4044                    }
4045                }),
4046            );
4047            if let ::core::result::Result::Err(payload) = __miniextendr_init_result {
4048                // package_init normally raises through its R guard. This is
4049                // the last-resort boundary for runtime/guard initialization:
4050                // no Rust-owned message or payload may survive Rf_error.
4051                ::core::mem::drop(payload);
4052                unsafe {
4053                    ::miniextendr_api::sys::Rf_error_unchecked(
4054                        c"miniextendr package initialization panicked before completing".as_ptr(),
4055                    );
4056                }
4057            }
4058        }
4059
4060        /// R_unload_<pkg> entry point — R calls this on `detach(unload=TRUE)` /
4061        /// `dyn.unload()`. Signals the miniextendr worker thread (if enabled)
4062        /// to exit cleanly. See `#103`.
4063        #[unsafe(no_mangle)]
4064        pub unsafe extern "C-unwind" fn #unload_name(
4065            _dll: *mut ::miniextendr_api::sys::DllInfo,
4066        ) {
4067            ::miniextendr_api::worker::miniextendr_runtime_shutdown();
4068        }
4069
4070        /// Linker anchor: stub.c takes the address of this symbol to force the
4071        /// linker to pull in the user crate's archive member from the staticlib.
4072        /// With codegen-units = 1, this single member contains all linkme
4073        /// distributed_slice entries. The name is package-independent so stub.c
4074        /// doesn't need configure substitution.
4075        ///
4076        /// Defined as a function rather than a static so it stays exported under
4077        /// the webR wasm RUSTFLAG -Zdefault-visibility=hidden, which keeps
4078        /// no_mangle functions exported (like the R_init entry point) but hides
4079        /// no_mangle statics. A hidden anchor breaks wasm side-module dlopen
4080        /// (bad export type, undefined). See miniextendr webR notes (#494).
4081        #[unsafe(no_mangle)]
4082        pub extern "C" fn miniextendr_force_link() {}
4083    };
4084
4085    expanded.into()
4086}
4087
4088#[cfg(test)]
4089mod tests;