Skip to main content

miniextendr_macros/miniextendr_impl_trait/
vtable.rs

1//! Vtable static generation, C wrapper generation, and method attribute parsing for trait impls.
2//!
3//! This module contains the core codegen for `#[miniextendr]` on `impl Trait for Type` blocks.
4//! It produces the vtable static constant, C-callable wrapper functions for each method and
5//! associated constant, and delegates to [`super::r_wrappers`] for R wrapper code generation.
6
7use proc_macro2::TokenStream;
8use quote::format_ident;
9use syn::ItemImpl;
10
11use super::r_wrappers::{TraitWrapperOpts, generate_trait_r_wrapper};
12use super::{TraitAbiMetadata, TraitConst, TraitMethod, type_to_symbol_name};
13use crate::miniextendr_impl::{ClassSystem, ImplAttrs};
14
15/// Generate the vtable static, C wrappers, R wrappers, and call defs for a trait implementation.
16///
17/// This is the main entry point for trait impl codegen. For a given
18/// `impl Trait for Type { ... }` block, it produces:
19///
20/// - The cleaned original impl block (with `#[miniextendr]` attrs stripped from methods)
21/// - A `static __VTABLE_{crate}_{Trait}_FOR_{Type}` with the declared repr(C) layout
22/// - C wrapper functions and `R_CallMethodDef` entries for each method and const
23/// - R wrapper code string (class-system specific)
24/// - Two `const` items: `{TYPE}_{TRAIT}_CALL_DEFS` and `R_WRAPPERS_{TYPE}_{TRAIT}_IMPL`
25///
26/// # Arguments
27///
28/// - `impl_item`: The parsed `impl Trait for Type` block
29/// - `trait_path`: Full path to the trait (e.g., `crate::Counter`)
30/// - `concrete_type`: The implementing type (e.g., `MyCounter`)
31/// - `impl_attrs`: The impl block's options. The class system picks the R
32///   wrapper generator; `blanket` skips emitting the impl block (a blanket impl
33///   already provides it); `internal` / `noexport` control the roxygen export
34///   tags; `no_preconditions` drops the R-side type checks, as on an inherent
35///   impl.
36/// - `abi`: The canonical trait name, fingerprint and callback layout from the declaration.
37pub(super) fn generate_vtable_static(
38    impl_item: &ItemImpl,
39    trait_path: &syn::Path,
40    concrete_type: &syn::Type,
41    impl_attrs: &ImplAttrs,
42    abi: &TraitAbiMetadata,
43    declaration: &[syn::TraitItemFn],
44    defaults: &[syn::ImplItemFn],
45) -> TokenStream {
46    let trait_name = &abi.name;
47
48    // Extract type identifier (for simple types)
49    let type_ident = match concrete_type {
50        syn::Type::Path(type_path) => {
51            let Some(last_seg) = type_path.path.segments.last() else {
52                return syn::Error::new_spanned(
53                    concrete_type,
54                    "type path must have at least one segment",
55                )
56                .into_compile_error();
57            };
58            last_seg.ident.clone()
59        }
60        _ => ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("Unknown"))
            }), ::quote::__private::Option::None)format_ident!("Unknown"),
61    };
62
63    // Extract type name for naming (simplified - handles Path types)
64    let type_name_str = type_to_symbol_name(concrete_type);
65
66    // Generate names (crate-prefixed vtable static — #1273 — routed through the
67    // shared naming.rs helper so this can't drift from the other two
68    // reconstruction sites in `miniextendr_impl_trait.rs`)
69    let vtable_static_name = crate::naming::vtable_static_ident(trait_name, &type_name_str);
70
71    // The impl block renders no page under `@noRd` or a plain `noexport`
72    // (`generate_trait_r_wrapper`'s `suppress_all_rd`); its methods' docs, of
73    // which S3 / vctrs keep the `@param` lines, then keep no links (#1818).
74    let impl_page = crate::roxygen::RdPage::suppressed_if(
75        crate::roxygen::attrs_have_roxygen_tag(&impl_item.attrs, "noRd")
76            || (impl_attrs.noexport && !impl_attrs.internal),
77    );
78
79    // Parse methods and consts from the impl block
80    let mut wrapper_impl = impl_item.clone();
81    wrapper_impl
82        .items
83        .extend(defaults.iter().cloned().map(syn::ImplItem::Fn));
84    let mut all_methods = match extract_methods(&wrapper_impl, impl_page, declaration) {
85        Ok(m) => m,
86        Err(e) => return e.into_compile_error(),
87    };
88    for method in &mut all_methods {
89        method.inherited_signature = defaults
90            .iter()
91            .any(|default| default.sig.ident == method.ident);
92    }
93    let consts = extract_consts(impl_item);
94
95    // Separate skipped methods: skipped methods are kept in the emitted impl block
96    // but excluded from C wrappers, R wrappers and call defs. The declaration
97    // alone determines vtable membership, including when an impl hides a wrapper.
98    let methods: Vec<&TraitMethod> = all_methods.iter().filter(|m| !m.skip).collect();
99
100    // Generate C wrappers and call defs for each non-skipped method
101    let method_c_wrappers: Vec<TokenStream> = methods
102        .iter()
103        .map(|m| generate_trait_method_c_wrapper(m, &type_ident, trait_name, trait_path))
104        .collect();
105
106    // Generate C wrappers for consts
107    let const_c_wrappers: Vec<TokenStream> = consts
108        .iter()
109        .map(|c| generate_trait_const_c_wrapper(c, &type_ident, trait_name, trait_path))
110        .collect();
111
112    // Combine C wrappers
113    let c_wrappers: Vec<TokenStream> = method_c_wrappers
114        .into_iter()
115        .chain(const_c_wrappers)
116        .collect();
117
118    // Check if impl block has @noRd doc comment (strip @param from class-level tags)
119    let raw_impl_tags = crate::roxygen::roxygen_tags_from_attrs(&impl_item.attrs, impl_page);
120    let (impl_doc_tags, param_warnings) = crate::roxygen::strip_method_tags(
121        &raw_impl_tags,
122        &type_ident.to_string(),
123        impl_item.impl_token.span,
124    );
125    let class_has_no_rd = crate::roxygen::has_roxygen_tag(&impl_doc_tags, "noRd");
126
127    // Generate R wrapper code string based on class system (only non-skipped methods)
128    let methods_owned: Vec<TraitMethod> = methods.iter().map(|m| (*m).clone()).collect();
129    let r_wrapper_string = match generate_trait_r_wrapper(
130        &type_ident,
131        trait_name,
132        &methods_owned,
133        &consts,
134        TraitWrapperOpts {
135            class_system: impl_attrs.class_system,
136            class_has_no_rd,
137            internal: impl_attrs.internal,
138            noexport: impl_attrs.noexport,
139            impl_preconditions: impl_attrs.preconditions,
140        },
141    ) {
142        Ok(s) => s,
143        Err(e) => return e.into_compile_error(),
144    };
145
146    // Generate constant name for R wrapper registration
147    let r_wrappers_const = crate::naming::trait_impl_wrappers_ident(&type_name_str, trait_name);
148
149    // Generate trait dispatch entry name
150    let dispatch_entry_name = crate::naming::trait_dispatch_ident(trait_name, &type_name_str);
151
152    let trait_tag = &abi.tag;
153
154    // Format R wrapper as raw string literal
155    let r_wrapper_str = crate::r_wrapper_raw_literal(
156        &r_wrapper_string,
157        &crate::r_shadowing::formal_names(
158            methods_owned
159                .iter()
160                .map(|m| (&m.sig.inputs, Default::default())),
161        ),
162    );
163    let source_loc_doc = crate::source_location_doc(type_ident.span());
164    let source_start = type_ident.span().start();
165    let source_line_lit = syn::LitInt::new(&source_start.line.to_string(), type_ident.span());
166    let source_col_lit =
167        syn::LitInt::new(&(source_start.column + 1).to_string(), type_ident.span());
168
169    // Strip #[miniextendr(...)] attrs from methods before emitting,
170    // so they don't trigger another macro expansion.
171    //
172    // Skip emitting the impl block when:
173    // - Body is empty (no methods, no consts) — blanket impl provides it
174    // - `blanket` flag is set — a blanket impl exists, methods are only for
175    //   C wrapper signature extraction, not for actual trait implementation
176    let has_items = !all_methods.is_empty() || !consts.is_empty();
177    let clean_impl_tokens = if has_items && !impl_attrs.blanket {
178        let mut clean_impl = impl_item.clone();
179        for item in &mut clean_impl.items {
180            if let syn::ImplItem::Fn(method) = item {
181                method
182                    .attrs
183                    .retain(|attr| !attr.path().is_ident("miniextendr"));
184                crate::trait_receiver::rewrite(&mut method.sig, Some(&mut method.block));
185            }
186        }
187        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&clean_impl, &mut _s);
    _s
}quote::quote! { #clean_impl }
188    } else {
189        ::quote::__private::TokenStream::new();quote::quote! {}
190    };
191
192    let vtable_static_tokens =
193        match generate_impl_vtable(abi, concrete_type, trait_path, &all_methods, declaration) {
194            Ok(tokens) => tokens,
195            Err(error) => return error.into_compile_error(),
196        };
197
198    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&clean_impl_tokens, &mut _s);
    ::quote::ToTokens::to_tokens(&param_warnings, &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, "\"Vtable 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(&concrete_type, &mut _s);
                            _s
                        });
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\"` implementing `\"");
                    ::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(&trait_path, &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::__private::parse(&mut _s,
                "\"Generated by `#[miniextendr]` on the trait impl block.\"");
            _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, "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::ToTokens::to_tokens(&vtable_static_tokens, &mut _s);
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut c_wrappers, i) = c_wrappers.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let c_wrappers =
                match c_wrappers.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&c_wrappers, &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,
                        "\"R wrapper code 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(&type_ident, &mut _s);
                            _s
                        });
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\"` implementing `\"");
                    ::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(&trait_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, &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, "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_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_ident(&mut _s, "static");
    ::quote::ToTokens::to_tokens(&r_wrappers_const, &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, "TraitImpl");
            ::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 impl `\"");
                    ::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(&trait_name, &mut _s);
                            _s
                        });
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\"` 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(&type_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);
    ::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,
                        "\"Trait dispatch entry: `\"");
                    ::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(&trait_name, &mut _s);
                            _s
                        });
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::parse(&mut _s, "\"` 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(&type_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, "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_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_TRAIT_DISPATCH");
                            _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_ident(&mut _s, "static");
    ::quote::ToTokens::to_tokens(&dispatch_entry_name, &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, "TraitDispatchEntry");
    ::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, "TraitDispatchEntry");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "concrete_tag");
            ::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, "abi");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "mx_tag_from_path");
            ::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, "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::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "core");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "module_path");
                            ::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::__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(&type_ident, &mut _s);
                                    _s
                                });
                            _s
                        });
                    _s
                });
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_ident(&mut _s, "trait_tag");
            ::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, "abi");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "mx_tag_from_path");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&trait_tag, &mut _s);
                    _s
                });
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_ident(&mut _s, "vtable");
            ::quote::__private::push_colon(&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, "std");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "ptr");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "from_ref");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_and(&mut _s);
                            ::quote::ToTokens::to_tokens(&vtable_static_name, &mut _s);
                            _s
                        });
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "cast");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_lt(&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, "os");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "raw");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "c_void");
                    ::quote::__private::push_gt(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    _s
                });
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_ident(&mut _s, "vtable_symbol");
            ::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, "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(&vtable_static_name, &mut _s);
                    _s
                });
            ::quote::__private::push_comma(&mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    _s
}quote::quote! {
199        // Pass through the original impl block (with method attrs stripped)
200        // — omitted when the body is empty (blanket impl covers it)
201        #clean_impl_tokens
202
203        // Warnings for @param tags on trait impl blocks
204        #param_warnings
205
206        #[doc = ::core::concat!(
207            "Vtable for `",
208            ::core::stringify!(#concrete_type),
209            "` implementing `",
210            ::core::stringify!(#trait_path),
211            "`."
212        )]
213        #[doc = "Generated by `#[miniextendr]` on the trait impl block."]
214        #[doc = #source_loc_doc]
215        #[doc = ::core::concat!("Generated from source file `", ::core::file!(), "`.")]
216        #[doc(hidden)]
217        #vtable_static_tokens
218
219        // C wrappers and call method defs for trait methods
220        #(#c_wrappers)*
221
222        // R wrapper registration via distributed slice
223        #[doc = ::core::concat!(
224            "R wrapper code for `",
225            ::core::stringify!(#type_ident),
226            "` implementing `",
227            ::core::stringify!(#trait_name),
228            "`."
229        )]
230        #[doc = #source_loc_doc]
231        #[doc = ::core::concat!("Generated from source file `", ::core::file!(), "`.")]
232        #[doc(hidden)]
233        #[cfg_attr(not(target_arch = "wasm32"), ::miniextendr_api::linkme::distributed_slice(::miniextendr_api::registry::MX_R_WRAPPERS), linkme(crate = ::miniextendr_api::linkme))]
234        #[allow(non_upper_case_globals)]
235        static #r_wrappers_const: ::miniextendr_api::registry::RWrapperEntry =
236            ::miniextendr_api::registry::RWrapperEntry {
237                priority: ::miniextendr_api::registry::RWrapperPriority::TraitImpl,
238                source_file: ::core::file!(),
239                source_line: #source_line_lit,
240                content: ::core::concat!(
241                    "# Generated from Rust impl `",
242                    ::core::stringify!(#trait_name),
243                    "` for `",
244                    ::core::stringify!(#type_ident),
245                    "` (",
246                    ::core::file!(),
247                    ":",
248                    #source_line_lit,
249                    ":",
250                    #source_col_lit,
251                    ")",
252                    #r_wrapper_str
253                ),
254            };
255
256        // Trait dispatch entry for universal_query
257        #[doc = ::core::concat!(
258            "Trait dispatch entry: `",
259            ::core::stringify!(#trait_name),
260            "` for `",
261            ::core::stringify!(#type_ident),
262            "`."
263        )]
264        #[doc = #source_loc_doc]
265        #[doc = ::core::concat!("Generated from source file `", ::core::file!(), "`.")]
266        #[doc(hidden)]
267        #[cfg_attr(not(target_arch = "wasm32"), ::miniextendr_api::linkme::distributed_slice(::miniextendr_api::registry::MX_TRAIT_DISPATCH), linkme(crate = ::miniextendr_api::linkme))]
268        #[allow(non_upper_case_globals)]
269        static #dispatch_entry_name: ::miniextendr_api::registry::TraitDispatchEntry =
270            ::miniextendr_api::registry::TraitDispatchEntry {
271                concrete_tag: ::miniextendr_api::abi::mx_tag_from_path(
272                    ::core::concat!(::core::module_path!(), "::", ::core::stringify!(#type_ident))
273                ),
274                trait_tag: ::miniextendr_api::abi::mx_tag_from_path(#trait_tag),
275                vtable: unsafe {
276                    // SAFETY: vtable is a static reference valid for program lifetime
277                    ::std::ptr::from_ref(&#vtable_static_name).cast::<::std::os::raw::c_void>()
278                },
279                vtable_symbol: ::core::stringify!(#vtable_static_name),
280            };
281    }
282}
283
284/// Reproduce the declaration's repr(C) layout locally, with its exact field
285/// order and callback types. The versioned declaration tag guards the cast to
286/// the consumer's nominal vtable type. The exported static's name and byte
287/// representation stay the same for native and wasm registration.
288///
289/// Non-generic callbacks belong to the trait, keeping default methods and
290/// declaration-local argument types in their original scope. Generic traits
291/// retain the existing concrete-shim path to avoid recursive conversion bounds.
292pub(super) fn generate_impl_vtable(
293    abi: &TraitAbiMetadata,
294    concrete_type: &syn::Type,
295    trait_path: &syn::Path,
296    methods: &[TraitMethod],
297    declaration: &[syn::TraitItemFn],
298) -> syn::Result<TokenStream> {
299    let trait_name = &abi.name;
300    let type_name = type_to_symbol_name(concrete_type);
301    let static_name = crate::naming::vtable_static_ident(trait_name, &type_name);
302    let layout_name = crate::naming::vtable_layout_ident(trait_name, &type_name);
303    let fields = &abi.fields;
304    let type_ident = match concrete_type {
305        syn::Type::Path(path) => path.path.segments.last().unwrap().ident.clone(),
306        _ => ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("Unknown"))
            }), ::quote::__private::Option::None)format_ident!("Unknown"),
307    };
308    let mut concrete_methods = Vec::new();
309    let mut initializers = Vec::new();
310    for field in &fields.named {
311        let name = field.ident.as_ref().unwrap();
312        let callback = if abi.generic {
313            let Some(method) = methods.iter().find(|method| method.ident == *name) else {
314                let message = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this impl must define the required trait method `{0}`",
                name))
    })format!("this impl must define the required trait method `{name}`");
315                return Err(syn::Error::new_spanned(trait_path, message));
316            };
317            concrete_methods.push(method.clone());
318            let shim = crate::naming::vtshim_ident(&type_ident, trait_name, name);
319            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&shim, &mut _s);
    _s
}quote::quote!(#shim)
320        } else {
321            let shim = crate::naming::trait_method_shim_ident(trait_name, name);
322            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&concrete_type, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::ToTokens::to_tokens(&trait_path, &mut _s);
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&shim, &mut _s);
    _s
}quote::quote!(<#concrete_type as #trait_path>::#shim)
323        };
324        initializers.push({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_colon(&mut _s);
    ::quote::ToTokens::to_tokens(&callback, &mut _s);
    _s
}quote::quote!(#name: #callback));
325    }
326    let shims = generate_concrete_vtable_shims(
327        &concrete_methods,
328        &type_ident,
329        trait_name,
330        trait_path,
331        concrete_type,
332        declaration,
333    );
334    Ok({
    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_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_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, "repr");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "C");
                    _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_camel_case_types");
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "pub");
    ::quote::__private::push_ident(&mut _s, "struct");
    ::quote::ToTokens::to_tokens(&layout_name, &mut _s);
    ::quote::ToTokens::to_tokens(&fields, &mut _s);
    ::quote::ToTokens::to_tokens(&shims, &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_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_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_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_ident(&mut _s, "pub");
    ::quote::__private::push_ident(&mut _s, "static");
    ::quote::ToTokens::to_tokens(&static_name, &mut _s);
    ::quote::__private::push_colon(&mut _s);
    ::quote::ToTokens::to_tokens(&layout_name, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::ToTokens::to_tokens(&layout_name, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            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 initializers, i) = initializers.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let initializers =
                        match initializers.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&initializers, &mut _s);
                }
            }
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    _s
}quote::quote! {
335        #[doc(hidden)]
336        #[repr(C)]
337        #[allow(non_camel_case_types)]
338        pub struct #layout_name #fields
339
340        #shims
341        #[doc(hidden)]
342        #[unsafe(no_mangle)]
343        #[allow(non_upper_case_globals)]
344        pub static #static_name: #layout_name = #layout_name {
345            #(#initializers),*
346        };
347    })
348}
349
350/// Generate concrete vtable shims for a generic trait impl.
351///
352/// For generic traits (e.g., `RExtend<T>`), shims and the vtable builder function
353/// cannot be generated at the trait definition site because where clauses like
354/// `Vec<T>: TryFromSexp` cause recursive trait resolution overflow. Instead, we
355/// generate fully monomorphized shims at the impl site where `T` is known (e.g., `T = i32`).
356///
357/// Each instance method gets a concrete `unsafe extern "C"` shim named
358/// `__vtshim_{crate}_{Type}__{Trait}__{method}` (crate-prefixed for webR
359/// cross-package symbol uniqueness — #1273) that, inside
360/// `with_r_unwind_protect_shim`, follows the declaration shim's steps:
361/// 1. Checks argument arity
362/// 2. Refuses aliased native borrows among the arguments
363/// 3. Extracts SEXP arguments to concrete Rust types
364/// 4. Calls the method via fully-qualified syntax `<Type as Trait>::method()`
365/// 5. Converts the result back to SEXP
366///
367/// Static methods are skipped (not part of the vtable).
368fn generate_concrete_vtable_shims(
369    methods: &[TraitMethod],
370    type_ident: &syn::Ident,
371    trait_name: &syn::Ident,
372    trait_path: &syn::Path,
373    concrete_type: &syn::Type,
374    declaration: &[syn::TraitItemFn],
375) -> TokenStream {
376    let mut shims = Vec::new();
377
378    for method in methods {
379        if !method.has_self {
380            continue; // Static methods not in vtable
381        }
382
383        let method_ident = &method.ident;
384        let shim_name = crate::naming::vtshim_ident(type_ident, trait_name, method_ident);
385        // Match the declaration shim's hygiene: source argument names must
386        // not shadow ABI parameters or the generated receiver reference.
387        let data = syn::Ident::new("data", proc_macro2::Span::mixed_site());
388        let argc = syn::Ident::new("argc", proc_macro2::Span::mixed_site());
389        let argv = syn::Ident::new("argv", proc_macro2::Span::mixed_site());
390        let self_ref = syn::Ident::new("self_ref", proc_macro2::Span::mixed_site());
391        let cell = syn::Ident::new("__mx_cell", proc_macro2::Span::mixed_site());
392        let guard = syn::Ident::new("__mx_guard", proc_macro2::Span::mixed_site());
393        let owner_kind = crate::trait_receiver::owner_receiver(&method.sig);
394        let owner = syn::Ident::new("owner", proc_macro2::Span::mixed_site());
395        let owner_parameter = owner_kind.map(|_| {
396            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&owner, &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, "SEXP");
    ::quote::__private::push_comma(&mut _s);
    _s
}quote::quote! {
397                #owner: ::miniextendr_api::SEXP,
398            }
399        });
400
401        // Count non-self parameters
402        let param_count = method
403            .sig
404            .inputs
405            .iter()
406            .filter(|a| !#[allow(non_exhaustive_omitted_patterns)] match a {
    syn::FnArg::Receiver(_) => true,
    _ => false,
}matches!(a, syn::FnArg::Receiver(_)))
407            .count();
408        let expected_argc = param_count as i32;
409
410        let diagnostic_names = declared_param_names(declaration, method_ident)
411            .expect("trait metadata includes each callback's argument names");
412
413        // Generate argument extraction (concrete types, no generics)
414        let arg_extractions: Vec<TokenStream> = method
415            .sig
416            .inputs
417            .iter()
418            .filter(|a| !#[allow(non_exhaustive_omitted_patterns)] match a {
    syn::FnArg::Receiver(_) => true,
    _ => false,
}matches!(a, syn::FnArg::Receiver(_)))
419            .enumerate()
420            .map(|(i, arg)| {
421                if let syn::FnArg::Typed(pt) = arg {
422                    let name = if let syn::Pat::Ident(pat_ident) = pt.pat.as_ref() {
423                        pat_ident.ident.clone()
424                    } else {
425                        match ::quote::__private::IdentFragmentAdapter(&i) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("arg{0}", arg))
                    }), ::quote::__private::Option::None.or(arg.span())),
}format_ident!("arg{}", i)
426                    };
427                    crate::trait_self_ref::argument(
428                        &name,
429                        &diagnostic_names[i],
430                        &pt.ty,
431                        concrete_type,
432                        i,
433                        &argv,
434                        method
435                            .inherited_signature
436                            .then(|| crate::trait_signature::argument_ident(&name))
437                            .as_ref(),
438                    )
439                } else {
440                    ::quote::__private::TokenStream::new();quote::quote! {}
441                }
442            })
443            .collect();
444
445        // Collect param names for the method call
446        let param_names: Vec<syn::Ident> = method
447            .sig
448            .inputs
449            .iter()
450            .filter(|a| !#[allow(non_exhaustive_omitted_patterns)] match a {
    syn::FnArg::Receiver(_) => true,
    _ => false,
}matches!(a, syn::FnArg::Receiver(_)))
451            .enumerate()
452            .map(|(i, arg)| {
453                if let syn::FnArg::Typed(pt) = arg
454                    && let syn::Pat::Ident(pat_ident) = pt.pat.as_ref()
455                {
456                    return pat_ident.ident.clone();
457                }
458                match ::quote::__private::IdentFragmentAdapter(&i) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("arg{0}", arg))
                    }), ::quote::__private::Option::None.or(arg.span())),
}format_ident!("arg{}", i)
459            })
460            .collect();
461
462        let targets = method.inherited_signature.then(|| {
463            param_names
464                .iter()
465                .map(crate::trait_signature::argument_ident)
466                .collect::<Vec<_>>()
467        });
468        let type_setup = method.inherited_signature.then(|| {
469            crate::trait_signature::setup(
470                &method.sig.inputs,
471                method.has_self,
472                &crate::trait_signature::callable(
473                    &method.sig,
474                    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&concrete_type, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::ToTokens::to_tokens(&trait_path, &mut _s);
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&method_ident, &mut _s);
    _s
}quote::quote!(<#concrete_type as #trait_path>::#method_ident),
475                ),
476            )
477        });
478        let native_borrow_checks = crate::trait_self_ref::native_borrow_checks(
479            param_names
480                .iter()
481                .zip(method.sig.inputs.iter().filter_map(|arg| match arg {
482                    syn::FnArg::Typed(pt) => Some(pt.ty.as_ref()),
483                    syn::FnArg::Receiver(_) => None,
484                }))
485                .enumerate()
486                .map(|(index, (_, ty))| (index, diagnostic_names[index].clone(), ty)),
487            &argv,
488            targets.as_deref(),
489        );
490
491        let self_refs: Vec<_> = param_names
492            .iter()
493            .zip(method.sig.inputs.iter().filter_map(|arg| match arg {
494                syn::FnArg::Typed(pt) => Some(pt.ty.as_ref()),
495                syn::FnArg::Receiver(_) => None,
496            }))
497            .enumerate()
498            .filter_map(|(index, (name, ty))| {
499                crate::type_inspect::self_ref_mutability(ty).map(|mutable| (name, mutable, index))
500            })
501            .collect();
502        let handles: Vec<_> = self_refs
503            .iter()
504            .map(|(name, mutable, index)| {
505                (
506                    crate::trait_self_ref::handle_ident(name),
507                    *mutable,
508                    diagnostic_names[*index].clone(),
509                )
510            })
511            .collect();
512        let alias_checks = crate::trait_self_ref::alias_checks(
513            concrete_type,
514            &handles,
515            Some((crate::trait_self_ref::cell_address(&cell), method.is_mut)),
516        );
517        let self_borrows = self_refs
518            .iter()
519            .map(|(name, mutable, _)| crate::trait_self_ref::borrow(name, *mutable));
520
521        // Generate method call using fully-qualified syntax to avoid ambiguity
522        // with generic trait paths like `RExtend<i32>::method()` where `<` would
523        // be parsed as a comparison operator in expression position.
524        // A visibility marker (`Invisible<T>`, #1213) is a transparent newtype
525        // around the real return; `method.sig.output` is already peeled, so
526        // unwrap it here before the conversion below.
527        let unwrap_marker = |call: TokenStream| -> TokenStream {
528            let call =
529                crate::type_inspect::prepare_return_value(call, method.invisible, method.serialize);
530            match &method.return_wrap {
531                Some(wrap) => wrap.prepare_value(call),
532                None => call,
533            }
534        };
535        let invocation = unwrap_marker({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&concrete_type, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::ToTokens::to_tokens(&trait_path, &mut _s);
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&method_ident, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&self_ref, &mut _s);
            ::quote::__private::push_comma(&mut _s);
            {
                use ::quote::__private::ext::*;
                let mut _first = true;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut param_names, i) = param_names.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let param_names =
                        match param_names.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&param_names, &mut _s);
                }
            }
            _s
        });
    _s
}quote::quote! {
536            <#concrete_type as #trait_path>::#method_ident(#self_ref, #(#param_names),*)
537        });
538        // The receiver borrows the value's RefCell for the rest of the shim,
539        // through the result conversion.
540        let receiver = if let Some(kind) = owner_kind {
541            let handle = kind.type_path();
542            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&self_ref, &mut _s);
    ::quote::__private::push_eq(&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(&handle, &mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_lt(&mut _s);
            ::quote::ToTokens::to_tokens(&concrete_type, &mut _s);
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "from_raw_parts");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_and(&mut _s);
                    ::quote::ToTokens::to_tokens(&owner, &mut _s);
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::ToTokens::to_tokens(&cell, &mut _s);
                    ::quote::__private::push_comma(&mut _s);
                    _s
                });
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    _s
}quote::quote! {
543                let #self_ref = unsafe {
544                    #handle::<#concrete_type>::from_raw_parts(
545                        &#owner,
546                        #cell,
547                    )
548                };
549            }
550        } else {
551            crate::trait_self_ref::receiver_borrow(&cell, &guard, &self_ref, method.is_mut)
552        };
553
554        // Generate result conversion
555        let has_return = match &method.sig.output {
556            syn::ReturnType::Default => false,
557            syn::ReturnType::Type(_, ty) => {
558                !#[allow(non_exhaustive_omitted_patterns)] match ty.as_ref() {
    syn::Type::Tuple(t) if t.elems.is_empty() => true,
    _ => false,
}matches!(ty.as_ref(), syn::Type::Tuple(t) if t.elems.is_empty())
559            }
560        };
561        let result_conversion = if has_return {
562            {
    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_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "trait_abi");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "to_sexp");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "result");
                    _s
                });
            _s
        });
    _s
}quote::quote! {
563                unsafe { ::miniextendr_api::trait_abi::to_sexp(result) }
564            }
565        } else {
566            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::__private::push_underscore(&mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_ident(&mut _s, "result");
    ::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, "trait_abi");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "nil");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                ::quote::__private::TokenStream::new());
            _s
        });
    _s
}quote::quote! {
567                let _ = result;
568                unsafe { ::miniextendr_api::trait_abi::nil() }
569            }
570        };
571
572        let method_name_str = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::{1}", trait_name,
                method_ident))
    })format!("{}::{}", trait_name, method_ident);
573
574        shims.push({
    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_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_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, "unsafe");
    ::quote::__private::push_ident(&mut _s, "extern");
    ::quote::__private::parse(&mut _s, "\"C\"");
    ::quote::__private::push_ident(&mut _s, "fn");
    ::quote::ToTokens::to_tokens(&shim_name, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&data, &mut _s);
            ::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, "std");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "os");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "raw");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "c_void");
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&owner_parameter, &mut _s);
            ::quote::ToTokens::to_tokens(&argc, &mut _s);
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_ident(&mut _s, "i32");
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&argv, &mut _s);
            ::quote::__private::push_colon(&mut _s);
            ::quote::__private::push_star(&mut _s);
            ::quote::__private::push_ident(&mut _s, "const");
            ::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);
            _s
        });
    ::quote::__private::push_rarrow(&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_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, "unwind_protect");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s,
                "with_r_unwind_protect_shim");
            ::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_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "miniextendr_api");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "trait_abi");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "check_arity");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::ToTokens::to_tokens(&argc, &mut _s);
                                    ::quote::__private::push_comma(&mut _s);
                                    ::quote::ToTokens::to_tokens(&expected_argc, &mut _s);
                                    ::quote::__private::push_comma(&mut _s);
                                    ::quote::ToTokens::to_tokens(&method_name_str, &mut _s);
                                    _s
                                });
                            ::quote::__private::push_semi(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "let");
                            ::quote::ToTokens::to_tokens(&cell, &mut _s);
                            ::quote::__private::push_eq(&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_and(&mut _s);
                                    ::quote::__private::push_star(&mut _s);
                                    ::quote::ToTokens::to_tokens(&data, &mut _s);
                                    ::quote::__private::push_dot(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "cast");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_lt(&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, "cell");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "RefCell");
                                    ::quote::__private::push_lt(&mut _s);
                                    ::quote::ToTokens::to_tokens(&concrete_type, &mut _s);
                                    ::quote::__private::push_shr(&mut _s);
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        ::quote::__private::TokenStream::new());
                                    _s
                                });
                            ::quote::__private::push_semi(&mut _s);
                            ::quote::ToTokens::to_tokens(&type_setup, &mut _s);
                            ::quote::ToTokens::to_tokens(&native_borrow_checks,
                                &mut _s);
                            {
                                use ::quote::__private::ext::*;
                                let has_iter = ::quote::__private::HasIterator::<false>;
                                #[allow(unused_mut)]
                                let (mut arg_extractions, i) =
                                    arg_extractions.quote_into_iter();
                                let has_iter = has_iter | i;
                                <_ as
                                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                while true {
                                    let arg_extractions =
                                        match arg_extractions.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    ::quote::ToTokens::to_tokens(&arg_extractions, &mut _s);
                                }
                            }
                            ::quote::ToTokens::to_tokens(&alias_checks, &mut _s);
                            {
                                use ::quote::__private::ext::*;
                                let has_iter = ::quote::__private::HasIterator::<false>;
                                #[allow(unused_mut)]
                                let (mut self_borrows, i) = self_borrows.quote_into_iter();
                                let has_iter = has_iter | i;
                                <_ as
                                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                while true {
                                    let self_borrows =
                                        match self_borrows.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    ::quote::ToTokens::to_tokens(&self_borrows, &mut _s);
                                }
                            }
                            ::quote::ToTokens::to_tokens(&receiver, &mut _s);
                            ::quote::__private::push_ident(&mut _s, "let");
                            ::quote::__private::push_ident(&mut _s, "result");
                            ::quote::__private::push_eq(&mut _s);
                            ::quote::ToTokens::to_tokens(&invocation, &mut _s);
                            ::quote::__private::push_semi(&mut _s);
                            ::quote::ToTokens::to_tokens(&result_conversion, &mut _s);
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote::quote! {
575            #[doc(hidden)]
576            #[allow(non_snake_case)]
577            unsafe extern "C" fn #shim_name(
578                #data: *mut ::std::os::raw::c_void,
579                #owner_parameter
580                #argc: i32,
581                #argv: *const ::miniextendr_api::SEXP,
582            ) -> ::miniextendr_api::SEXP {
583                // with_r_unwind_protect_shim: returns tagged error SEXP on panic
584                // so the View method wrapper can re-panic via repanic_if_rust_error,
585                // allowing rust_* class layering (issue #345).
586                ::miniextendr_api::unwind_protect::with_r_unwind_protect_shim(|| {
587                    ::miniextendr_api::trait_abi::check_arity(#argc, #expected_argc, #method_name_str);
588                    let #cell = unsafe { &*#data.cast::<::core::cell::RefCell<#concrete_type>>() };
589                    #type_setup
590                    #native_borrow_checks
591                    #(#arg_extractions)*
592                    #alias_checks
593                    #(#self_borrows)*
594                    #receiver
595                    let result = #invocation;
596                    #result_conversion
597                })
598            }
599        });
600    }
601
602    {
    let mut _s = ::quote::__private::TokenStream::new();
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut shims, i) = shims.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let shims =
                match shims.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&shims, &mut _s);
        }
    }
    _s
}quote::quote! { #(#shims)* }
603}
604
605/// Inherit only declaration-level R naming/export metadata. The implementation
606/// owns its execution options, docs and signatures; an explicit impl rename
607/// wins, while a declaration's skipped method is never exported accidentally.
608pub(super) fn inherit_method_metadata(
609    implementation: &mut ItemImpl,
610    declaration: &[syn::TraitItemFn],
611) -> syn::Result<()> {
612    for item in &mut implementation.items {
613        let syn::ImplItem::Fn(method) = item else {
614            continue;
615        };
616        let Some(declared) = declaration
617            .iter()
618            .find(|declared| declared.sig.ident == method.sig.ident)
619        else {
620            continue; // rustc diagnoses methods that do not belong to the trait.
621        };
622        let inherited = parse_trait_method_attrs(&declared.attrs)?;
623        let own = parse_trait_method_attrs(&method.attrs)?;
624        if own.r_name.is_none()
625            && let Some(r_name) = inherited.r_name
626        {
627            method
628                .attrs
629                .push(::syn::__private::parse_quote({
        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, "miniextendr");
                ::quote::__private::push_group(&mut _s,
                    ::quote::__private::Delimiter::Parenthesis,
                    {
                        let mut _s = ::quote::__private::TokenStream::new();
                        ::quote::__private::push_ident(&mut _s, "r_name");
                        ::quote::__private::push_eq(&mut _s);
                        ::quote::ToTokens::to_tokens(&r_name, &mut _s);
                        _s
                    });
                _s
            });
        _s
    })syn::parse_quote!(#[miniextendr(r_name = #r_name)]));
630        }
631        if inherited.skip && !own.skip {
632            method.attrs.push(::syn::__private::parse_quote({
        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, "miniextendr");
                ::quote::__private::push_group(&mut _s,
                    ::quote::__private::Delimiter::Parenthesis,
                    {
                        let mut _s = ::quote::__private::TokenStream::new();
                        ::quote::__private::push_ident(&mut _s, "skip");
                        _s
                    });
                _s
            });
        _s
    })syn::parse_quote!(#[miniextendr(skip)]));
633        }
634    }
635    Ok(())
636}
637
638/// The parameter names the declaration's metadata gives `method`, in order.
639/// The metadata names every parameter (`arg<i>` for a declaration pattern).
640fn declared_param_names(
641    declaration: &[syn::TraitItemFn],
642    method: &syn::Ident,
643) -> Option<Vec<String>> {
644    let declared = declaration
645        .iter()
646        .find(|declared| declared.sig.ident == *method)?;
647    Some(
648        declared
649            .sig
650            .inputs
651            .iter()
652            .filter_map(|arg| match arg {
653                syn::FnArg::Typed(arg) => Some(arg),
654                syn::FnArg::Receiver(_) => None,
655            })
656            .map(|arg| {
657                let syn::Pat::Ident(name) = arg.pat.as_ref() else {
658                    {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("metadata has named arguments")));
}unreachable!("metadata has named arguments")
659                };
660                crate::naming::ident_name(&name.ident)
661            })
662            .collect(),
663    )
664}
665
666/// Extract all methods from a trait impl block as [`TraitMethod`] structs.
667///
668/// Parses each `ImplItem::Fn` to determine receiver type, mutability,
669/// `#[miniextendr(...)]` attributes (coerce, skip, r_name, defaults, etc.),
670/// and roxygen `@param` tags from doc comments. `impl_page` is whether the
671/// impl block renders a help page: the methods' docs keep no links when it
672/// does not.
673pub(super) fn extract_methods(
674    impl_item: &ItemImpl,
675    impl_page: crate::roxygen::RdPage,
676    declaration: &[syn::TraitItemFn],
677) -> syn::Result<Vec<TraitMethod>> {
678    let mut methods = Vec::new();
679    for item in &impl_item.items {
680        if let syn::ImplItem::Fn(method) = item {
681            // Check receiver type
682            let (has_self, is_mut) = method.sig.inputs.first().map_or((false, false), |arg| {
683                if let syn::FnArg::Receiver(_) = arg {
684                    (true, crate::trait_receiver::is_mut(&method.sig))
685                } else {
686                    (false, false)
687                }
688            });
689            // The unforced dots are no trait-method parameter (#1892), nor is
690            // the RNG guard (#2002).
691            crate::miniextendr_fn::refuse_lazy_dots_in_trait_method(&method.sig.inputs)?;
692            crate::miniextendr_fn::refuse_rng_guard_in_trait_method(&method.sig.inputs)?;
693            let mut attrs = parse_trait_method_attrs(&method.attrs)?;
694            // Every parameter `choices(...)` / `inherits(...)` /
695            // `not_inherits(...)` / `no_na(...)` name must exist, as on
696            // inherent methods; `Option<T>` scalar
697            // choices params are the optional form (#1473).
698            crate::miniextendr_fn::finalize_method_param_attrs(
699                &mut attrs.per_param,
700                &method.sig.inputs,
701                &[],
702                &attrs.defaults,
703                attrs
704                    .per_param_span
705                    .unwrap_or_else(|| method.sig.ident.span()),
706            )?;
707            check_trait_method_defaults(
708                &attrs.defaults,
709                &method.sig.inputs,
710                attrs
711                    .defaults_span
712                    .unwrap_or_else(|| method.sig.ident.span()),
713            )?;
714
715            // The author's tags; the R wrapper generators pick the `@param`
716            // lines and the page tags from them (every tag but `@title` and a
717            // bare `@export` on blocks without `\usage`,
718            // `r_wrappers::forwarded_body_tag`).
719            let doc_tags = crate::roxygen::roxygen_tags_from_attrs(&method.attrs, impl_page);
720
721            // Validate and peel a return-visibility marker (#1213): the
722            // codegen sees the inner type, `.0` unwraps the value.
723            if let syn::ReturnType::Type(_, ty) = &method.sig.output
724                && let Some(err) = crate::type_inspect::visibility_marker_error(ty, "return")
725            {
726                return Err(err);
727            }
728            let (invisible, output) =
729                crate::type_inspect::peel_return_visibility(&method.sig.output);
730            let mut sig = method.sig.clone();
731            // Wrappers call the method positionally; patterns belong only to
732            // the original impl body. Give every wrapper argument a binding,
733            // including ignored and destructured parameters (#2029): the
734            // declaration's name, as the trait's shims report it, else
735            // `arg<i>`.
736            let declared = declared_param_names(declaration, &method.sig.ident);
737            let mut used_names: std::collections::HashSet<_> = sig
738                .inputs
739                .iter()
740                .filter_map(|arg| match arg {
741                    syn::FnArg::Typed(arg) => match arg.pat.as_ref() {
742                        syn::Pat::Ident(pat) => Some(crate::naming::ident_name(&pat.ident)),
743                        _ => None,
744                    },
745                    _ => None,
746                })
747                .collect();
748            for (index, arg) in sig
749                .inputs
750                .iter_mut()
751                .filter_map(|arg| match arg {
752                    syn::FnArg::Typed(arg) => Some(arg),
753                    _ => None,
754                })
755                .enumerate()
756            {
757                if !#[allow(non_exhaustive_omitted_patterns)] match arg.pat.as_ref() {
    syn::Pat::Ident(_) => true,
    _ => false,
}matches!(arg.pat.as_ref(), syn::Pat::Ident(_)) {
758                    let mut name = declared
759                        .as_ref()
760                        .and_then(|names| names.get(index).cloned())
761                        .unwrap_or_else(|| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("arg{0}", index))
    })format!("arg{index}"));
762                    while !used_names.insert(name.clone()) {
763                        name.push('_');
764                    }
765                    let name = crate::naming::name_ident(&name);
766                    arg.pat = ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::ToTokens::to_tokens(&name, &mut _s);
        _s
    })syn::parse_quote!(#name);
767                }
768            }
769            let (return_wrap, output) = crate::return_wrap::resolve(&output, attrs.wrap)?;
770            if return_wrap
771                .as_ref()
772                .is_some_and(|wrap| wrap.conversion.is_some())
773            {
774                return Err(syn::Error::new_spanned(
775                    &method.sig.output,
776                    "ConvertTo/ConvertFrom require an inherent S7 impl method",
777                ));
778            }
779            if return_wrap.is_some() && (attrs.serialize || attrs.unwrap_in_r) {
780                return Err(syn::Error::new_spanned(
781                    &method.sig.output,
782                    "explicit class wrapping cannot be combined with serialize or unwrap_in_r",
783                ));
784            }
785            sig.output = crate::type_inspect::serialize_return_type(&output, attrs.serialize);
786
787            methods.push(TraitMethod {
788                inherited_signature: false,
789                ident: method.sig.ident.clone(),
790                sig,
791                invisible,
792                has_self,
793                is_mut,
794                worker: attrs.worker,
795                unsafe_main_thread: attrs.unsafe_main_thread,
796                coerce: attrs.coerce,
797                check_interrupt: attrs.check_interrupt,
798                rng: attrs.rng,
799                unwrap_in_r: attrs.unwrap_in_r,
800                serialize: attrs.serialize,
801                return_wrap,
802                param_defaults: attrs.defaults,
803                doc_tags,
804                skip: attrs.skip,
805                r_name: attrs.r_name,
806                strict: attrs.strict,
807                lifecycle: attrs.lifecycle,
808                r_entry: attrs.r_entry,
809                r_post_checks: attrs.r_post_checks,
810                r_on_exit: attrs.r_on_exit,
811                no_shortcut: attrs.no_shortcut,
812                generic: attrs.generic,
813                receiver: attrs.receiver,
814                no_dispatch_dots: attrs.no_dispatch_dots,
815                per_param: attrs.per_param,
816                preconditions: attrs.preconditions,
817            });
818        }
819    }
820    Ok(methods)
821}
822
823/// Reject a `defaults(...)` entry that names no parameter of the trait
824/// method, with the inherent-impl wording (a typo would otherwise drop the
825/// default without a word).
826fn check_trait_method_defaults(
827    defaults: &std::collections::HashMap<String, String>,
828    inputs: &syn::punctuated::Punctuated<syn::FnArg, ::syn::token::Commasyn::Token![,]>,
829    span: proc_macro2::Span,
830) -> syn::Result<()> {
831    let names: std::collections::HashSet<String> = inputs
832        .iter()
833        .filter_map(|arg| match arg {
834            syn::FnArg::Typed(pt) => match pt.pat.as_ref() {
835                syn::Pat::Ident(pat_ident) => Some(crate::naming::ident_name(&pat_ident.ident)),
836                _ => None,
837            },
838            syn::FnArg::Receiver(_) => None,
839        })
840        .collect();
841    let mut invalid: Vec<&str> = defaults
842        .keys()
843        .map(String::as_str)
844        .filter(|key| !names.contains(key.strip_prefix("r#").unwrap_or(key)))
845        .collect();
846    if invalid.is_empty() {
847        return Ok(());
848    }
849    invalid.sort_unstable();
850    Err(syn::Error::new(
851        span,
852        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("defaults(...) references non-existent parameter(s): {0}",
                invalid.join(", ")))
    })format!(
853            "defaults(...) references non-existent parameter(s): {}",
854            invalid.join(", ")
855        ),
856    ))
857}
858
859/// Parsed `#[miniextendr(...)]` attributes for a single trait method.
860///
861/// Extracted from method-level attributes to control C wrapper behavior,
862/// threading, and R wrapper generation.
863struct TraitMethodAttrs {
864    /// Dispatch to worker thread. Set by explicit `#[miniextendr(worker)]` or `worker-default` feature.
865    worker: bool,
866    /// Force execution on R's main thread (overrides explicit or feature-selected worker dispatch).
867    unsafe_main_thread: bool,
868    /// Enable `Rf_coerceVector` for all parameters. Set by `coerce` /
869    /// `no_coerce`, else the `coerce-default` feature.
870    coerce: bool,
871    /// Call `R_CheckUserInterrupt` before the method body.
872    check_interrupt: bool,
873    /// Wrap the call in `GetRNGstate`/`PutRNGstate` for reproducible random number generation.
874    rng: bool,
875    /// Return `Result<T, E>` to R without unwrapping (R wrapper receives the result variant).
876    unwrap_in_r: bool,
877    /// Serialize the complete return value through `AsSerialize<T>`.
878    serialize: bool,
879    wrap: Option<ClassSystem>,
880    /// Exclude this method from all generated wrappers (C, R, vtable shims).
881    skip: bool,
882    /// Parameter default values: keys are parameter names, values are R expressions.
883    defaults: std::collections::HashMap<String, String>,
884    /// Override the R-facing method name.
885    r_name: Option<String>,
886    /// Strict output conversion: panic instead of lossy widening for i64/u64/isize/usize.
887    /// Set by `strict` / `no_strict`, else the `strict-default` feature.
888    strict: bool,
889    /// Lifecycle specification for deprecation/experimental status.
890    lifecycle: Option<crate::lifecycle::LifecycleSpec>,
891    /// R code to inject at the very top of the wrapper body.
892    r_entry: Option<String>,
893    /// R code to inject after all checks, immediately before `.Call()`.
894    r_post_checks: Option<String>,
895    /// Register `on.exit()` cleanup code in the R wrapper.
896    r_on_exit: Option<crate::miniextendr_fn::ROnExit>,
897    /// Opt out of the S7 fast-dispatch shortcut (`#[miniextendr(s7(no_shortcut))]`).
898    no_shortcut: bool,
899    generic: Option<String>,
900    receiver: Option<String>,
901    no_dispatch_dots: bool,
902    /// The method's bare `preconditions` / `no_preconditions` (#1017); the
903    /// last one written wins. See `TraitMethod::preconditions`.
904    preconditions: Option<bool>,
905    /// Per-parameter `match_arg`/`choices`/`several_ok` attributes, keyed by
906    /// Rust parameter name. See `TraitMethod::per_param`.
907    per_param: std::collections::HashMap<String, crate::miniextendr_fn::ParamAttrs>,
908    /// Span of the first `choices` / `choices_several_ok` / `no_default` /
909    /// `no_na` / `inherits` / `not_inherits` option: where an unknown
910    /// parameter name is reported.
911    per_param_span: Option<proc_macro2::Span>,
912    /// Span of the `defaults(...)` option: where an unknown parameter name in
913    /// it is reported.
914    defaults_span: Option<proc_macro2::Span>,
915}
916
917/// Parse `#[miniextendr(...)]` attributes from a trait method.
918///
919/// Supports two syntax styles:
920/// - **Flat**: `#[miniextendr(worker, coerce, rng)]`
921/// - **Nested class-system**: `#[miniextendr(env(worker, coerce))]`
922///
923/// Both styles can coexist. The `worker` flag controls whether static methods
924/// dispatch to the worker thread (defaults to `cfg!(feature = "worker-default")`).
925/// `coerce` and `strict` default to the `coerce-default` / `strict-default`
926/// features, as on inherent methods, and `no_coerce` / `no_strict` opt out.
927fn parse_trait_method_attrs(attrs: &[syn::Attribute]) -> syn::Result<TraitMethodAttrs> {
928    use syn::spanned::Spanned;
929    let mut worker = false;
930    let mut unsafe_main_thread = false;
931    let mut coerce: Option<bool> = None;
932    let mut check_interrupt = false;
933    let mut rng = false;
934    let mut unwrap_in_r = false;
935    let mut serialize = false;
936    let mut wrap = None;
937    let mut skip = false;
938    let mut strict: Option<bool> = None;
939    let mut defaults = std::collections::HashMap::new();
940    let mut r_name: Option<String> = None;
941    let mut lifecycle: Option<crate::lifecycle::LifecycleSpec> = None;
942    let mut r_entry: Option<String> = None;
943    let mut r_post_checks: Option<String> = None;
944    let mut r_on_exit: Option<crate::miniextendr_fn::ROnExit> = None;
945    let mut no_shortcut = false;
946    let mut generic = None;
947    let mut receiver = None;
948    let mut no_dispatch_dots = false;
949    let mut preconditions: Option<bool> = None;
950    let mut per_param: std::collections::HashMap<String, crate::miniextendr_fn::ParamAttrs> =
951        std::collections::HashMap::new();
952    let mut per_param_span: Option<proc_macro2::Span> = None;
953    let mut defaults_span: Option<proc_macro2::Span> = None;
954
955    for attr in attrs {
956        if !attr.path().is_ident("miniextendr") {
957            continue;
958        }
959
960        attr.parse_nested_meta(|meta| {
961            let is_class_meta = meta.path.is_ident("env")
962                || meta.path.is_ident("r6")
963                || meta.path.is_ident("s7")
964                || meta.path.is_ident("s3")
965                || meta.path.is_ident("s4");
966
967            if is_class_meta {
968                meta.parse_nested_meta(|inner| {
969                    if inner.path.is_ident("worker") {
970                        worker = true;
971                        crate::deprecation::worker_option(inner.path.span());
972                    } else if inner.path.is_ident("main_thread") {
973                        unsafe_main_thread = true;
974                    } else if inner.path.is_ident("coerce") {
975                        coerce = Some(true);
976                    } else if inner.path.is_ident("no_coerce") {
977                        coerce = Some(false);
978                    } else if inner.path.is_ident("check_interrupt") {
979                        check_interrupt = true;
980                    } else if inner.path.is_ident("unwrap_in_r") {
981                        unwrap_in_r = true;
982                    } else if inner.path.is_ident("wrap") {
983                        let value: syn::LitStr = inner.value()?.parse()?;
984                        wrap = Some(crate::return_wrap::parse_system(&value)?);
985                    } else if inner.path.is_ident("serialize") {
986                        serialize = true;
987                    } else if inner.path.is_ident("no_shortcut") {
988                        no_shortcut = true;
989                    } else if (meta.path.is_ident("s3") || meta.path.is_ident("s4"))
990                        && inner.path.is_ident("generic")
991                    {
992                        let value: syn::LitStr = inner.value()?.parse()?;
993                        if generic
994                            .replace(crate::naming::parse_generic(&value)?)
995                            .is_some()
996                        {
997                            return Err(inner.error("generic may be specified only once"));
998                        }
999                    } else if (meta.path.is_ident("s3") || meta.path.is_ident("s4"))
1000                        && inner.path.is_ident("receiver")
1001                    {
1002                        let value: syn::LitStr = inner.value()?.parse()?;
1003                        if receiver.replace(crate::naming::parse_r_receiver(&value)?).is_some() {
1004                            return Err(inner.error("receiver may be specified only once"));
1005                        }
1006                    } else if (meta.path.is_ident("s3") || meta.path.is_ident("s4"))
1007                        && inner.path.is_ident("no_dots")
1008                    {
1009                        no_dispatch_dots = true;
1010                    } else {
1011                        return Err(inner.error(
1012                            "unknown nested option; expected `worker`, `main_thread`, `coerce`, \
1013                             `no_coerce`, `check_interrupt`, `unwrap_in_r`, `serialize`, or \
1014                             `no_shortcut` (S3/S4 also accept `generic`, `receiver`, `no_dots`)",
1015                        ));
1016                    }
1017                    Ok(())
1018                })?;
1019            } else if meta.path.is_ident("worker") {
1020                worker = true;
1021                crate::deprecation::worker_option(meta.path.span());
1022            } else if meta.path.is_ident("main_thread") {
1023                unsafe_main_thread = true;
1024            } else if meta.path.is_ident("coerce") {
1025                coerce = Some(true);
1026            } else if meta.path.is_ident("no_coerce") {
1027                coerce = Some(false);
1028            } else if meta.path.is_ident("check_interrupt") {
1029                check_interrupt = true;
1030            } else if meta.path.is_ident("rng") {
1031                if meta.input.peek(syn::token::Paren) {
1032                    return crate::miniextendr_fn::refuse_rng_seed_on_method(
1033                        &meta,
1034                        "a trait method",
1035                    );
1036                }
1037                rng = true;
1038            } else if meta.path.is_ident("unwrap_in_r") {
1039                unwrap_in_r = true;
1040            } else if meta.path.is_ident("wrap") {
1041                let value: syn::LitStr = meta.value()?.parse()?;
1042                wrap = Some(crate::return_wrap::parse_system(&value)?);
1043            } else if meta.path.is_ident("serialize") {
1044                serialize = true;
1045            } else if meta.path.is_ident("skip") {
1046                skip = true;
1047            } else if meta.path.is_ident("no_shortcut") {
1048                no_shortcut = true;
1049            } else if meta.path.is_ident("r_name") {
1050                let value: syn::LitStr = meta.value()?.parse()?;
1051                r_name = Some(value.value());
1052            } else if meta.path.is_ident("strict") {
1053                strict = Some(true);
1054            } else if meta.path.is_ident("no_strict") {
1055                strict = Some(false);
1056            } else if meta.path.is_ident("defaults") {
1057                // Parse defaults(param = "value", param2 = "value2", ...)
1058                defaults_span.get_or_insert(meta.path.span());
1059                meta.parse_nested_meta(|inner| {
1060                    let param_name = inner
1061                        .path
1062                        .get_ident()
1063                        .map(|i| i.to_string())
1064                        .unwrap_or_default();
1065                    let value: syn::LitStr = inner.value()?.parse()?;
1066                    defaults.insert(param_name, value.value());
1067                    Ok(())
1068                })?;
1069            } else if meta.path.is_ident("lifecycle") {
1070                if meta.input.peek(::syn::token::Eqsyn::Token![=]) {
1071                    // lifecycle = "stage"
1072                    let _: ::syn::token::Eqsyn::Token![=] = meta.input.parse()?;
1073                    let value: syn::LitStr = meta.input.parse()?;
1074                    let stage = crate::lifecycle::LifecycleStage::from_str(&value.value())
1075                        .ok_or_else(|| {
1076                            syn::Error::new(
1077                                value.span(),
1078                                "invalid lifecycle stage; expected one of: experimental, stable, superseded, soft-deprecated, deprecated, defunct",
1079                            )
1080                        })?;
1081                    lifecycle = Some(crate::lifecycle::LifecycleSpec::new(stage));
1082                } else {
1083                    // lifecycle(stage = "deprecated", when = "0.4.0", ...)
1084                    let mut spec = crate::lifecycle::LifecycleSpec::default();
1085                    meta.parse_nested_meta(|inner| {
1086                        let key = inner.path.get_ident()
1087                            .ok_or_else(|| inner.error("expected identifier"))?
1088                            .to_string();
1089                        let _: ::syn::token::Eqsyn::Token![=] = inner.input.parse()?;
1090                        let value: syn::LitStr = inner.input.parse()?;
1091                        match key.as_str() {
1092                            "stage" => {
1093                                spec.stage = crate::lifecycle::LifecycleStage::from_str(&value.value())
1094                                    .ok_or_else(|| syn::Error::new(value.span(), "invalid lifecycle stage"))?;
1095                            }
1096                            "when" => spec.when = Some(value.value()),
1097                            "what" => spec.what = Some(value.value()),
1098                            "with" => spec.with = Some(value.value()),
1099                            "details" => spec.details = Some(value.value()),
1100                            "id" => spec.id = Some(value.value()),
1101                            _ => return Err(inner.error(
1102                                "unknown lifecycle option; expected: stage, when, what, with, details, id"
1103                            )),
1104                        }
1105                        Ok(())
1106                    })?;
1107                    lifecycle = Some(spec);
1108                }
1109            } else if meta.path.is_ident("r_entry") {
1110                let _: ::syn::token::Eqsyn::Token![=] = meta.input.parse()?;
1111                let value: syn::LitStr = meta.input.parse()?;
1112                r_entry = Some(value.value());
1113            } else if meta.path.is_ident("r_post_checks") {
1114                let _: ::syn::token::Eqsyn::Token![=] = meta.input.parse()?;
1115                let value: syn::LitStr = meta.input.parse()?;
1116                r_post_checks = Some(value.value());
1117            } else if meta.path.is_ident("r_on_exit") {
1118                if meta.input.peek(::syn::token::Eqsyn::Token![=]) {
1119                    // Short form: r_on_exit = "expr"
1120                    let _: ::syn::token::Eqsyn::Token![=] = meta.input.parse()?;
1121                    let value: syn::LitStr = meta.input.parse()?;
1122                    r_on_exit = Some(crate::miniextendr_fn::ROnExit {
1123                        expr: value.value(),
1124                        add: true,
1125                        after: true,
1126                    });
1127                } else {
1128                    // Long form: r_on_exit(expr = "...", add = false, after = false)
1129                    let mut expr = None;
1130                    let mut add = true;
1131                    let mut after = true;
1132                    meta.parse_nested_meta(|inner| {
1133                        if inner.path.is_ident("expr") {
1134                            let _: ::syn::token::Eqsyn::Token![=] = inner.input.parse()?;
1135                            let value: syn::LitStr = inner.input.parse()?;
1136                            expr = Some(value.value());
1137                        } else if inner.path.is_ident("add") {
1138                            let _: ::syn::token::Eqsyn::Token![=] = inner.input.parse()?;
1139                            let value: syn::LitBool = inner.input.parse()?;
1140                            add = value.value;
1141                        } else if inner.path.is_ident("after") {
1142                            let _: ::syn::token::Eqsyn::Token![=] = inner.input.parse()?;
1143                            let value: syn::LitBool = inner.input.parse()?;
1144                            after = value.value;
1145                        } else {
1146                            return Err(inner.error(
1147                                "unknown r_on_exit option; expected `expr`, `add`, or `after`",
1148                            ));
1149                        }
1150                        Ok(())
1151                    })?;
1152                    let expr = expr.ok_or_else(|| {
1153                        meta.error("r_on_exit(...) requires `expr = \"...\"` specifying the R expression")
1154                    })?;
1155                    r_on_exit = Some(crate::miniextendr_fn::ROnExit { expr, add, after });
1156                }
1157            } else if meta.path.is_ident("choices") {
1158                per_param_span.get_or_insert(meta.path.span());
1159                // `choices(param = "a, b, c", param2 = "x, y")` — explicit string choice lists.
1160                //
1161                // NOTE: `match_arg`/`match_arg_several_ok` (choices derived from a
1162                // `#[derive(MatchArg)]` enum's `CHOICES` const) are intentionally NOT
1163                // supported here yet. The inherent-impl path additionally emits a
1164                // `MX_MATCH_ARG_CHOICES` registration
1165                // (`generate_method_match_arg_entries` in
1166                // `miniextendr_impl.rs`) that resolves the write-time placeholder
1167                // formal default into the enum's literal choice list — trait methods
1168                // have no equivalent metadata registration yet, so accepting `match_arg`
1169                // here would silently ship a formal default that never resolves
1170                // (`object '.__MX_MATCH_ARG_CHOICES_...__' not found` at call time).
1171                // `choices(...)` needs no such resolution (the literal list is baked in
1172                // at macro-expansion time), so it's safe to support today. Tracked in
1173                // the trait-method-emitter follow-up issue for full match_arg parity.
1174                meta.parse_nested_meta(|inner| {
1175                    let name = crate::miniextendr_fn::method_check_param(&inner)?;
1176                    let _: ::syn::token::Eqsyn::Token![=] = inner.input.parse()?;
1177                    let value: syn::LitStr = inner.input.parse()?;
1178                    let choices = crate::r_wrapper_builder::split_choice_list(&value.value());
1179                    per_param.entry(name).or_default().choices = Some(choices);
1180                    Ok(())
1181                })?;
1182            } else if meta.path.is_ident("choices_several_ok") {
1183                per_param_span.get_or_insert(meta.path.span());
1184                // `choices_several_ok(param = "a, b, c")` — choices + several_ok.
1185                meta.parse_nested_meta(|inner| {
1186                    let name = crate::miniextendr_fn::method_check_param(&inner)?;
1187                    let _: ::syn::token::Eqsyn::Token![=] = inner.input.parse()?;
1188                    let value: syn::LitStr = inner.input.parse()?;
1189                    let choices = crate::r_wrapper_builder::split_choice_list(&value.value());
1190                    let entry = per_param.entry(name).or_default();
1191                    entry.choices = Some(choices);
1192                    entry.several_ok = true;
1193                    Ok(())
1194                })?;
1195            } else if meta.path.is_ident("no_default") {
1196                per_param_span.get_or_insert(meta.path.span());
1197                // `no_default(p, q)` — choice params whose R formal has no
1198                // default (#1828).
1199                crate::miniextendr_fn::parse_method_no_default(&meta, &mut per_param)?;
1200            } else if meta.path.is_ident("preconditions") || meta.path.is_ident("no_preconditions")
1201            {
1202                // Bare: the whole method keeps / drops its type-derived R-side
1203                // checks. `preconditions(p)` / `no_preconditions(p)`: those
1204                // parameters. The last one written wins.
1205                let keep = meta.path.is_ident("preconditions");
1206                if meta.input.peek(syn::token::Paren) {
1207                    per_param_span.get_or_insert(meta.path.span());
1208                    crate::miniextendr_fn::parse_method_preconditions(&meta, &mut per_param, keep)?;
1209                } else {
1210                    preconditions = Some(keep);
1211                }
1212            } else if meta.path.is_ident("no_na") {
1213                per_param_span.get_or_insert(meta.path.span());
1214                // `no_na(p, q(message = "..."))` — R-side `!anyNA(p)` checks.
1215                crate::miniextendr_fn::parse_method_no_na(&meta, &mut per_param)?;
1216            } else if let Some(check) = crate::miniextendr_fn::ClassCheck::of(&meta.path) {
1217                per_param_span.get_or_insert(meta.path.span());
1218                // `inherits(p = "cls_a, cls_b", q(class = "cls", message = "..."))` —
1219                // R-side `inherits(p, c(...))` checks; `not_inherits(...)` the
1220                // same shape, `!inherits(p, c(...))`.
1221                crate::miniextendr_fn::parse_method_class_check(&meta, &mut per_param, check)?;
1222            } else if meta.path.is_ident("formal") {
1223                return Err(meta.error(crate::miniextendr_fn::TRAIT_FORMAL_UNSUPPORTED));
1224            } else {
1225                return Err(meta.error(
1226                    "unknown #[miniextendr] option on trait impl method; expected one of: \
1227                     `env`, `r6`, `s7`, `s3`, `s4`, `worker`, `main_thread`, `coerce`, `no_coerce`, \
1228                     `check_interrupt`, `rng`, `unwrap_in_r`, `serialize`, `skip`, `no_shortcut`, `r_name`, \
1229                     `defaults`, `strict`, `no_strict`, `lifecycle`, `r_entry`, `r_post_checks`, `r_on_exit`, \
1230                     `choices`, `choices_several_ok`, `no_default`, `inherits`, `not_inherits`, `no_na`, \
1231                     `preconditions`, `no_preconditions`",
1232                ));
1233            }
1234            Ok(())
1235        })?;
1236    }
1237
1238    if serialize && unwrap_in_r {
1239        return Err(syn::Error::new(
1240            proc_macro2::Span::call_site(),
1241            "`serialize` cannot be combined with `unwrap_in_r`: it serializes the complete return value, including any Result variant",
1242        ));
1243    }
1244
1245    Ok(TraitMethodAttrs {
1246        worker: worker || falsecfg!(feature = "worker-default"),
1247        unsafe_main_thread,
1248        coerce: coerce.unwrap_or(falsecfg!(feature = "coerce-default")),
1249        check_interrupt,
1250        rng,
1251        unwrap_in_r,
1252        serialize,
1253        wrap,
1254        skip,
1255        strict: strict.unwrap_or(falsecfg!(feature = "strict-default")),
1256        defaults,
1257        r_name,
1258        lifecycle,
1259        r_entry,
1260        r_post_checks,
1261        r_on_exit,
1262        no_shortcut,
1263        generic,
1264        receiver,
1265        no_dispatch_dots,
1266        preconditions,
1267        per_param,
1268        per_param_span,
1269        defaults_span,
1270    })
1271}
1272
1273/// Extract associated constant items from a trait impl block.
1274///
1275/// Each `const NAME: Type = value;` in the impl block becomes a [`TraitConst`]
1276/// that will get its own zero-argument C wrapper for R access.
1277fn extract_consts(impl_item: &ItemImpl) -> Vec<TraitConst> {
1278    impl_item
1279        .items
1280        .iter()
1281        .filter_map(|item| {
1282            if let syn::ImplItem::Const(const_item) = item {
1283                Some(TraitConst {
1284                    ident: const_item.ident.clone(),
1285                    ty: const_item.ty.clone(),
1286                })
1287            } else {
1288                None
1289            }
1290        })
1291        .collect()
1292}
1293
1294/// Generate a C wrapper function and `R_CallMethodDef` for a single trait method.
1295///
1296/// Uses `CWrapperContext` builder to produce:
1297/// - An `extern "C-unwind"` function callable from R via `.Call()`
1298/// - A `R_CallMethodDef` constant for symbol registration
1299///
1300/// Instance methods (`has_self`) extract the object via `ErasedExternalPtr::from_sexp`
1301/// and call with fully-qualified trait syntax `<Type as Trait>::method(self_ref, ...)`.
1302/// Static methods run on the worker thread when the `worker` flag is set.
1303///
1304/// `&Self` / `&mut Self` parameters are borrowed external arguments (#2106): the C wrapper
1305/// converts each to its `ExternalPtr<Type>` handle and borrows it with the declared
1306/// mutability for the call, after checking that no two of them alias while one is mutable.
1307pub(super) fn generate_trait_method_c_wrapper(
1308    method: &TraitMethod,
1309    type_ident: &syn::Ident,
1310    trait_name: &syn::Ident,
1311    trait_path: &syn::Path,
1312) -> TokenStream {
1313    use crate::c_wrapper_builder::{CWrapperContext, ReturnHandling, ThreadStrategy};
1314
1315    let method_ident = &method.ident;
1316    let c_ident = method.c_wrapper_ident(type_ident, trait_name);
1317    let call_method_def_ident = method.call_method_def_ident(type_ident, trait_name);
1318
1319    // Thread strategy: instance methods stay on main thread (self_ref can't cross threads);
1320    // static methods use worker thread only when worker=true (explicit or worker-default feature)
1321    // and, as for standalone functions, no R value crosses the call and no
1322    // `check_interrupt` asks for R's interrupt check.
1323    let thread_strategy = if method.has_self
1324        || method.unsafe_main_thread
1325        || method.check_interrupt
1326        || crate::type_inspect::signature_holds_r_values(&method.sig)
1327    {
1328        ThreadStrategy::MainThread
1329    } else if method.worker {
1330        ThreadStrategy::WorkerThread
1331    } else {
1332        ThreadStrategy::MainThread
1333    };
1334
1335    // Build rust argument names from the signature (excluding self receiver)
1336    let rust_args: Vec<syn::Ident> = method
1337        .sig
1338        .inputs
1339        .iter()
1340        .filter_map(|arg| {
1341            if let syn::FnArg::Typed(pt) = arg {
1342                if let syn::Pat::Ident(pat_ident) = pt.pat.as_ref() {
1343                    Some(pat_ident.ident.clone())
1344                } else {
1345                    None
1346                }
1347            } else {
1348                None
1349            }
1350        })
1351        .collect();
1352
1353    // Filter inputs to exclude the receiver (builder handles self separately with has_self()).
1354    // `&Self` / `&mut Self` parameters stay: the builder converts them to their handles and
1355    // borrows them for the call (#2106). Track them for the alias checks.
1356    let mut self_ref_params = Vec::new();
1357    let filtered_inputs: syn::punctuated::Punctuated<syn::FnArg, ::syn::token::Commasyn::Token![,]> = method
1358        .sig
1359        .inputs
1360        .iter()
1361        .filter(|arg| !#[allow(non_exhaustive_omitted_patterns)] match arg {
    syn::FnArg::Receiver(_) => true,
    _ => false,
}matches!(arg, syn::FnArg::Receiver(_)))
1362        .inspect(|arg| {
1363            if let syn::FnArg::Typed(pt) = arg
1364                && let Some(mutable) = crate::type_inspect::self_ref_mutability(&pt.ty)
1365                && let syn::Pat::Ident(pat_ident) = pt.pat.as_ref()
1366            {
1367                self_ref_params.push((pat_ident.ident.clone(), mutable));
1368            }
1369        })
1370        .cloned()
1371        .collect();
1372    let call_args = &rust_args;
1373
1374    // Determine return handling
1375    let return_handling = if method.unwrap_in_r
1376        && crate::return_type_analysis::output_is_result(&method.sig.output)
1377    {
1378        ReturnHandling::IntoR
1379    } else {
1380        crate::c_wrapper_builder::detect_return_handling(
1381            &method.sig.output,
1382            method.return_wrap.as_ref(),
1383        )
1384    };
1385
1386    // Generate R wrapper const name (not actually used but needed by builder)
1387    let r_wrappers_const = crate::naming::trait_impl_wrappers_ident(
1388        &crate::naming::ident_name(type_ident),
1389        trait_name,
1390    );
1391
1392    // Build the wrapper using the builder infrastructure
1393    // Use custom call_method_def_ident to avoid collisions with inherent impl methods
1394    // Messages R sees name the method as R calls it (`r_name`).
1395    let mut builder = CWrapperContext::builder(method_ident.clone(), c_ident)
1396        .r_name(method.r_method_name())
1397        .r_wrapper_const(r_wrappers_const)
1398        .inputs(filtered_inputs)
1399        .output(method.sig.output.clone())
1400        .thread_strategy(thread_strategy)
1401        .return_handling(return_handling)
1402        .type_context(type_ident.clone())
1403        .call_method_def_ident(call_method_def_ident);
1404
1405    if method.inherited_signature {
1406        builder = builder.inferred_signature(crate::trait_signature::callable(
1407            &method.sig,
1408            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&type_ident, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::ToTokens::to_tokens(&trait_path, &mut _s);
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&method_ident, &mut _s);
    _s
}quote::quote!(<#type_ident as #trait_path>::#method_ident),
1409        ));
1410    }
1411
1412    let mut post_conversion = Vec::new();
1413    let handles: Vec<_> = self_ref_params
1414        .iter()
1415        .map(|(name, mutable)| {
1416            (
1417                crate::externalptr_args::handle_ident(name),
1418                *mutable,
1419                crate::naming::ident_name(name),
1420            )
1421        })
1422        .collect();
1423    let receiver = method
1424        .has_self
1425        .then(|| ({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "__miniextendr_receiver_addr");
    _s
}quote::quote!(__miniextendr_receiver_addr), method.is_mut));
1426    post_conversion.push(crate::trait_self_ref::alias_checks(
1427        type_ident, &handles, receiver,
1428    ));
1429
1430    if method.has_self {
1431        let self_sexp = crate::naming::self_sexp_ident();
1432        let owner = syn::Ident::new("__mx_owner", proc_macro2::Span::mixed_site());
1433        let self_ptr = syn::Ident::new("self_ptr", proc_macro2::Span::mixed_site());
1434        let self_ref = syn::Ident::new("self_ref", proc_macro2::Span::mixed_site());
1435        let cell = syn::Ident::new("__mx_cell", proc_macro2::Span::mixed_site());
1436        let guard = syn::Ident::new("__mx_guard", proc_macro2::Span::mixed_site());
1437        let resolve = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&owner, &mut _s);
    ::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, "externalptr");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ReceiverOwner");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "resolve");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&type_ident, &mut _s);
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&self_sexp, &mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    _s
}quote::quote! {
1438            let #owner =
1439                ::miniextendr_api::externalptr::ReceiverOwner::resolve::<#type_ident>(#self_sexp);
1440        };
1441        let self_extraction = if let Some(kind) = crate::trait_receiver::owner_receiver(&method.sig)
1442        {
1443            let receiver_mutability = method.is_mut.then(|| {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "mut");
    _s
}quote::quote!(mut));
1444            let borrow = match kind {
1445                crate::trait_receiver::OwnerReceiver::Shared => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "borrow");
    _s
}quote::quote!(borrow),
1446                crate::trait_receiver::OwnerReceiver::Mutable => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "borrow_mut");
    _s
}quote::quote!(borrow_mut),
1447            };
1448            let address = (!self_ref_params.is_empty()).then(|| {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::__private::push_ident(&mut _s, "__miniextendr_receiver_addr");
    ::quote::__private::push_eq(&mut _s);
    ::quote::ToTokens::to_tokens(&self_ptr, &mut _s);
    ::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_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "cast");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_lt(&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, "ffi");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "c_void");
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    ::quote::__private::push_semi(&mut _s);
    _s
}quote::quote! {
1449                let __miniextendr_receiver_addr = #self_ptr.as_ptr().cast::<::core::ffi::c_void>();
1450            });
1451            // The alias preflight above runs before constructing either an
1452            // exclusive view or any &Self argument reference. The view holds
1453            // the value's RefCell borrow for the rest of the call.
1454            post_conversion.push({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&self_ref, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::ToTokens::to_tokens(&self_ptr, &mut _s);
    ::quote::__private::push_dot(&mut _s);
    ::quote::ToTokens::to_tokens(&borrow, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    ::quote::__private::push_semi(&mut _s);
    _s
}quote::quote! {
1455                let #self_ref = #self_ptr.#borrow();
1456            });
1457            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&resolve, &mut _s);
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&receiver_mutability, &mut _s);
    ::quote::ToTokens::to_tokens(&self_ptr, &mut _s);
    ::quote::__private::push_eq(&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, "externalptr");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "wrap_receiver");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_lt(&mut _s);
            ::quote::ToTokens::to_tokens(&type_ident, &mut _s);
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&owner, &mut _s);
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "get");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    _s
                });
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    ::quote::ToTokens::to_tokens(&address, &mut _s);
    _s
}quote::quote! {
1458                #resolve
1459                let #receiver_mutability #self_ptr = unsafe {
1460                    ::miniextendr_api::externalptr::wrap_receiver::<#type_ident>(#owner.get())
1461                };
1462                #address
1463            }
1464        } else {
1465            let trait_method_name = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::{1}()", trait_name,
                method.r_method_name()))
    })format!("{}::{}()", trait_name, method.r_method_name());
1466            // Borrow after argument conversion and alias preflight: an R
1467            // error that longjmps out of a conversion would skip the
1468            // borrow's release.
1469            post_conversion.push(crate::trait_self_ref::receiver_borrow(
1470                &cell,
1471                &guard,
1472                &self_ref,
1473                method.is_mut,
1474            ));
1475            let self_setup = (!self_ref_params.is_empty()).then(|| {
1476                let address = crate::trait_self_ref::cell_address(&cell);
1477                {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::__private::push_ident(&mut _s, "__miniextendr_receiver_addr");
    ::quote::__private::push_eq(&mut _s);
    ::quote::ToTokens::to_tokens(&address, &mut _s);
    ::quote::__private::push_semi(&mut _s);
    _s
}quote::quote! {
1478                    let __miniextendr_receiver_addr = #address;
1479                }
1480            });
1481            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&resolve, &mut _s);
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&self_ptr, &mut _s);
    ::quote::__private::push_eq(&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, "externalptr");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "ErasedExternalPtr");
            ::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::ToTokens::to_tokens(&owner, &mut _s);
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "get");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    _s
                });
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&cell, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_ident(&mut _s, "match");
    ::quote::ToTokens::to_tokens(&self_ptr, &mut _s);
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "downcast_cell");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&type_ident, &mut _s);
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    ::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, "option");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Option");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Some");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "cell");
                    _s
                });
            ::quote::__private::push_fat_arrow(&mut _s);
            ::quote::__private::push_ident(&mut _s, "cell");
            ::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, "option");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Option");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "None");
            ::quote::__private::push_fat_arrow(&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, "externalptr");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s,
                "handle_downcast_failed_in");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_lt(&mut _s);
            ::quote::ToTokens::to_tokens(&type_ident, &mut _s);
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_and(&mut _s);
                    ::quote::ToTokens::to_tokens(&self_ptr, &mut _s);
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::ToTokens::to_tokens(&trait_method_name, &mut _s);
                    ::quote::__private::push_comma(&mut _s);
                    _s
                });
            ::quote::__private::push_comma(&mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    ::quote::ToTokens::to_tokens(&self_setup, &mut _s);
    _s
}quote::quote! {
1482                #resolve
1483                let #self_ptr = unsafe {
1484                    ::miniextendr_api::externalptr::ErasedExternalPtr::from_sexp(#owner.get())
1485                };
1486                let #cell = match #self_ptr.downcast_cell::<#type_ident>() {
1487                    ::core::option::Option::Some(cell) => cell,
1488                    ::core::option::Option::None => ::miniextendr_api::externalptr::handle_downcast_failed_in::<#type_ident>(
1489                        &#self_ptr, #trait_method_name,
1490                    ),
1491                };
1492                #self_setup
1493            }
1494        };
1495
1496        // Call expression with self_ref and dereferenced &Self params
1497        // Use fully-qualified syntax to avoid ambiguity with generic traits
1498        // (e.g., `RExtend<i32>::method()` is ambiguous — `<` parsed as comparison)
1499        let call_expr = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&type_ident, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::ToTokens::to_tokens(&trait_path, &mut _s);
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&method_ident, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&self_ref, &mut _s);
            ::quote::__private::push_comma(&mut _s);
            {
                use ::quote::__private::ext::*;
                let mut _first = true;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut call_args, i) = call_args.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let call_args =
                        match call_args.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&call_args, &mut _s);
                }
            }
            _s
        });
    _s
}quote::quote! {
1500            <#type_ident as #trait_path>::#method_ident(#self_ref, #(#call_args),*)
1501        };
1502        // Unwrap a visibility-marker newtype (#1213); the output type is peeled.
1503        let call_expr = crate::type_inspect::prepare_return_value(
1504            call_expr,
1505            method.invisible,
1506            method.serialize,
1507        );
1508        let call_expr = match &method.return_wrap {
1509            Some(wrap) => wrap.prepare_value(call_expr),
1510            None => call_expr,
1511        };
1512
1513        builder = builder
1514            .pre_call(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [self_extraction]))vec![self_extraction])
1515            .call_expr(call_expr)
1516            .has_self();
1517    } else {
1518        // Static method: call directly without self
1519        let call_expr = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&type_ident, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::ToTokens::to_tokens(&trait_path, &mut _s);
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&method_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 call_args, i) = call_args.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let call_args =
                        match call_args.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&call_args, &mut _s);
                }
            }
            _s
        });
    _s
}quote::quote! {
1520            <#type_ident as #trait_path>::#method_ident(#(#call_args),*)
1521        };
1522        let call_expr = crate::type_inspect::prepare_return_value(
1523            call_expr,
1524            method.invisible,
1525            method.serialize,
1526        );
1527        let call_expr = match &method.return_wrap {
1528            Some(wrap) => wrap.prepare_value(call_expr),
1529            None => call_expr,
1530        };
1531
1532        builder = builder.call_expr(call_expr);
1533    }
1534
1535    // Apply coerce_all if the method has #[miniextendr(coerce)]
1536    if method.coerce {
1537        builder = builder.coerce_all();
1538    }
1539
1540    // Apply check_interrupt if the method has #[miniextendr(check_interrupt)]
1541    if method.check_interrupt {
1542        builder = builder.check_interrupt();
1543    }
1544
1545    // Apply rng if the method has #[miniextendr(rng)]
1546    if method.rng {
1547        builder = builder.rng();
1548    }
1549
1550    // Apply strict mode for lossy type conversions
1551    if method.strict {
1552        builder = builder.strict();
1553    }
1554
1555    // A `choices(...)` string under an `Either<.., R>` layer decodes with the
1556    // choice-or-other split instead of `TryFromSexp` (the other layers of a
1557    // `choices` string convert through `TryFromSexp` directly).
1558    // `no_na(..)` parameters also have their converted value checked, for what
1559    // the type reads as `NA` beyond the R guard's `anyNA()`.
1560    for (rust_name, attrs) in &method.per_param {
1561        if let Some(leaf) = attrs.layered_leaf() {
1562            builder = builder.layered_choice(rust_name.clone(), leaf);
1563        }
1564        if attrs.checks.no_na {
1565            builder = builder.no_na(rust_name.clone(), attrs.checks.no_na_message.clone());
1566        }
1567    }
1568
1569    // The builder generates both the C wrapper and the R_CallMethodDef
1570    builder.post_conversion(post_conversion).build().generate()
1571}
1572
1573/// Generate a C wrapper function and `R_CallMethodDef` for a trait associated constant.
1574///
1575/// The generated wrapper takes only the call-attribution slot and returns the constant value
1576/// converted to SEXP. Uses fully-qualified syntax `<Type as Trait>::CONST`
1577/// to access the value. Always runs on the main thread.
1578pub(super) fn generate_trait_const_c_wrapper(
1579    trait_const: &TraitConst,
1580    type_ident: &syn::Ident,
1581    trait_name: &syn::Ident,
1582    trait_path: &syn::Path,
1583) -> TokenStream {
1584    use crate::c_wrapper_builder::{CWrapperContext, ThreadStrategy};
1585
1586    let const_ident = &trait_const.ident;
1587    let c_ident = trait_const.c_wrapper_ident(type_ident, trait_name);
1588    let call_method_def_ident = trait_const.call_method_def_ident(type_ident, trait_name);
1589    let const_ty = &trait_const.ty;
1590
1591    // Generate R wrapper const name
1592    let r_wrappers_const = crate::naming::trait_impl_wrappers_ident(
1593        &crate::naming::ident_name(type_ident),
1594        trait_name,
1595    );
1596
1597    // Build the call expression to access the const
1598    let call_expr = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&type_ident, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::ToTokens::to_tokens(&trait_path, &mut _s);
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&const_ident, &mut _s);
    _s
}quote::quote! {
1599        <#type_ident as #trait_path>::#const_ident
1600    };
1601
1602    // Determine return type handling - we need to convert the const to SEXP
1603    // The return type is `-> Type` not just `Type`
1604    let return_type: syn::ReturnType = ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_rarrow(&mut _s);
        ::quote::ToTokens::to_tokens(&const_ty, &mut _s);
        _s
    })syn::parse_quote!(-> #const_ty);
1605    let return_handling = crate::c_wrapper_builder::detect_return_handling(&return_type, None);
1606
1607    // Build wrapper - no inputs, just returns the const value
1608    let builder = CWrapperContext::builder(const_ident.clone(), c_ident)
1609        .r_wrapper_const(r_wrappers_const)
1610        .inputs(Default::default()) // no inputs
1611        .output(return_type)
1612        .call_expr(call_expr)
1613        .thread_strategy(ThreadStrategy::MainThread)
1614        .return_handling(return_handling)
1615        .type_context(type_ident.clone())
1616        .call_method_def_ident(call_method_def_ident);
1617
1618    builder.build().generate()
1619}