Skip to main content

miniextendr_macros/
miniextendr_impl_trait.rs

1//! # `#[miniextendr]` on trait impls - Trait Implementation Registration
2//!
3//! This module handles `#[miniextendr]` applied to trait implementations,
4//! generating the vtable static for cross-package trait dispatch, plus optional
5//! R-callable wrappers for direct method access.
6//!
7//! ## Overview
8//!
9//! When `#[miniextendr]` is applied to an `impl Trait for Type` block, it:
10//!
11//! 1. **Detects the trait** from the impl syntax (no attribute args needed)
12//! 2. **Generates vtable static** using declaration-provided layout and callbacks
13//! 3. **Generates C wrappers** for each trait method (for R `.Call` access)
14//! 4. **Generates R wrapper code** for the trait methods
15//! 5. **Passes through** the original impl block unchanged
16//!
17//! ## Usage
18//!
19//! ```ignore
20//! use miniextendr_api::miniextendr;
21//!
22//! // The trait must have been defined with #[miniextendr]
23//! // which generates __Counter_build_vtable::<T>()
24//!
25//! struct MyCounter { value: i32 }
26//!
27//! #[miniextendr]
28//! impl Counter for MyCounter {
29//!     fn value(&self) -> i32 {
30//!         self.value
31//!     }
32//!     fn increment(&mut self) {
33//!         self.value += 1;
34//!     }
35//!     fn add(&mut self, n: i32) {
36//!         self.value += n;
37//!     }
38//! }
39//! ```
40//!
41//! Generates (conceptually):
42//!
43//! ```ignore
44//! // Original impl block (passed through)
45//! impl Counter for MyCounter {
46//!     fn value(&self) -> i32 { self.value }
47//!     fn increment(&mut self) { self.value += 1; }
48//!     fn add(&mut self, n: i32) { self.value += n; }
49//! }
50//!
51//! // Generated vtable static (crate-prefixed for webR cross-package symbol
52//! // uniqueness — #1273; example assumes a crate named `mypkg`). Its local
53//! // repr(C) type reproduces Counter's declared callback order and types.
54//! pub static __VTABLE_mypkg_Counter_FOR_MyCounter: __VTABLE_mypkg_Counter_FOR_MyCounter_Layout =
55//!     __VTABLE_mypkg_Counter_FOR_MyCounter_Layout {
56//!         value: <MyCounter as Counter>::__Counter_value_shim,
57//!         increment: <MyCounter as Counter>::__Counter_increment_shim,
58//!         add: <MyCounter as Counter>::__Counter_add_shim,
59//!     };
60//! ```
61//!
62//! ## How It Works
63//!
64//! 1. Parse `impl Trait for Type` to extract trait path and concrete type
65//! 2. Generate vtable static name: `__VTABLE_{crate}_{Trait}_FOR_{Type}`
66//! 3. Obtain the declaration's ABI tag and ordered callback fields through its metadata macro
67//! 4. Initialize the local layout with associated or concrete generic callbacks
68//!
69//! ## Trait Detection
70//!
71//! The macro reads the trait directly from the impl syntax:
72//!
73//! ```ignore
74//! #[miniextendr]
75//! impl path::to::Counter for MyType { ... }
76//! //   ^^^^^^^^^^^^^^^^^ detected automatically
77//! ```
78//!
79//! No extra arguments are needed; the trait path is explicit in the impl syntax.
80//!
81//! ## Name Generation
82//!
83//! - **Vtable static**: `__VTABLE_{crate}_{Trait}_FOR_{Type}` (case-preserving;
84//!   `{crate}` is the consuming crate's name, prefixed for webR cross-package symbol
85//!   uniqueness — #1273)
86//! - **Local vtable type**: the static name with `_Layout` appended
87//!
88//! For `impl foo::Counter for my_mod::MyType` in a crate named `mypkg`:
89//! - Static: `__VTABLE_mypkg_Counter_FOR_MyType`
90//! - Callback: `<my_mod::MyType as foo::Counter>::__Counter_value_shim`
91//!
92//! ## Integration with ExternalPtr / TypedExternal
93//!
94//! The generated vtable static is automatically registered via linkme
95//! distributed slices.
96//!
97//! ```ignore
98//! #[derive(ExternalPtr)]
99//! struct MyCounter { value: i32 }
100//!
101//! #[miniextendr]
102//! impl Counter for MyCounter { /* ... */ }
103//! ```
104//!
105//! `ExternalPtr<T>` provides the type identity for the external pointer.
106//! Trait dispatch is wired automatically.
107//!
108//! ## Thread Safety
109//!
110//! The generated vtable is a static constant, safe to access from any thread.
111//! Trait shims now mirror inherent impls: instance methods stay on the main
112//! thread, while static trait methods run on the worker thread unless
113//! `main_thread` is explicitly requested.
114
115use proc_macro2::TokenStream;
116use quote::{ToTokens, format_ident};
117use syn::ItemImpl;
118
119use crate::miniextendr_impl::{ClassSystem, ImplAttrs};
120
121/// Parsed method from a trait impl block.
122///
123/// Stores everything needed to generate C wrappers, R wrappers, and vtable
124/// shims for a single method in an `impl Trait for Type` block.
125#[derive(#[automatically_derived]
impl ::core::fmt::Debug for TraitMethod {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        let names: &'static _ =
            &["inherited_signature", "ident", "sig", "invisible", "has_self",
                        "is_mut", "worker", "unsafe_main_thread", "coerce",
                        "check_interrupt", "rng", "unwrap_in_r", "serialize",
                        "return_wrap", "param_defaults", "doc_tags", "skip",
                        "r_name", "strict", "lifecycle", "r_entry", "r_post_checks",
                        "r_on_exit", "no_shortcut", "generic", "receiver",
                        "no_dispatch_dots", "per_param", "preconditions"];
        let values: &[&dyn ::core::fmt::Debug] =
            &[&self.inherited_signature, &self.ident, &self.sig,
                        &self.invisible, &self.has_self, &self.is_mut, &self.worker,
                        &self.unsafe_main_thread, &self.coerce,
                        &self.check_interrupt, &self.rng, &self.unwrap_in_r,
                        &self.serialize, &self.return_wrap, &self.param_defaults,
                        &self.doc_tags, &self.skip, &self.r_name, &self.strict,
                        &self.lifecycle, &self.r_entry, &self.r_post_checks,
                        &self.r_on_exit, &self.no_shortcut, &self.generic,
                        &self.receiver, &self.no_dispatch_dots, &self.per_param,
                        &&self.preconditions];
        ::core::fmt::Formatter::debug_struct_fields_finish(f, "TraitMethod",
            names, values)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for TraitMethod {
    #[inline]
    fn clone(&self) -> Self {
        TraitMethod {
            inherited_signature: ::core::clone::Clone::clone(&self.inherited_signature),
            ident: ::core::clone::Clone::clone(&self.ident),
            sig: ::core::clone::Clone::clone(&self.sig),
            invisible: ::core::clone::Clone::clone(&self.invisible),
            has_self: ::core::clone::Clone::clone(&self.has_self),
            is_mut: ::core::clone::Clone::clone(&self.is_mut),
            worker: ::core::clone::Clone::clone(&self.worker),
            unsafe_main_thread: ::core::clone::Clone::clone(&self.unsafe_main_thread),
            coerce: ::core::clone::Clone::clone(&self.coerce),
            check_interrupt: ::core::clone::Clone::clone(&self.check_interrupt),
            rng: ::core::clone::Clone::clone(&self.rng),
            unwrap_in_r: ::core::clone::Clone::clone(&self.unwrap_in_r),
            serialize: ::core::clone::Clone::clone(&self.serialize),
            return_wrap: ::core::clone::Clone::clone(&self.return_wrap),
            param_defaults: ::core::clone::Clone::clone(&self.param_defaults),
            doc_tags: ::core::clone::Clone::clone(&self.doc_tags),
            skip: ::core::clone::Clone::clone(&self.skip),
            r_name: ::core::clone::Clone::clone(&self.r_name),
            strict: ::core::clone::Clone::clone(&self.strict),
            lifecycle: ::core::clone::Clone::clone(&self.lifecycle),
            r_entry: ::core::clone::Clone::clone(&self.r_entry),
            r_post_checks: ::core::clone::Clone::clone(&self.r_post_checks),
            r_on_exit: ::core::clone::Clone::clone(&self.r_on_exit),
            no_shortcut: ::core::clone::Clone::clone(&self.no_shortcut),
            generic: ::core::clone::Clone::clone(&self.generic),
            receiver: ::core::clone::Clone::clone(&self.receiver),
            no_dispatch_dots: ::core::clone::Clone::clone(&self.no_dispatch_dots),
            per_param: ::core::clone::Clone::clone(&self.per_param),
            preconditions: ::core::clone::Clone::clone(&self.preconditions),
        }
    }
}Clone)]
126struct TraitMethod {
127    /// Signature copied from declaration metadata: infer its argument types
128    /// from the real method so declaration-local aliases keep their identity.
129    inherited_signature: bool,
130    /// Rust method identifier (e.g., `value`, `increment`).
131    ident: syn::Ident,
132    /// Full method signature including self receiver and all parameters. A
133    /// return-visibility marker is already peeled (see `invisible`).
134    sig: syn::Signature,
135    /// Return-type visibility marker (#1213): `Some(true)` for
136    /// `-> Invisible<T>`, `Some(false)` for `-> Visible<T>`, stripped from
137    /// `sig.output`. The C wrapper unwraps the newtype before conversion.
138    invisible: Option<bool>,
139    /// Whether the method has a receiver (`&self`, `&mut self`).
140    /// False for static/associated methods.
141    has_self: bool,
142    /// Whether receiver is `&mut self` (vs `&self`). Only meaningful if `has_self` is true.
143    is_mut: bool,
144    /// When true, dispatches to the worker thread via `run_on_worker`.
145    /// Set by explicit `#[miniextendr(worker)]` or the `worker-default` feature flag.
146    worker: bool,
147    /// When true, forces execution on R's main thread via `unsafe(main_thread)`.
148    unsafe_main_thread: bool,
149    /// Enable automatic type coercion for all parameters via `Rf_coerceVector`.
150    coerce: bool,
151    /// Check for R user interrupts (`R_CheckUserInterrupt`) before calling the method.
152    check_interrupt: bool,
153    /// Enable RNG state management (`GetRNGstate`/`PutRNGstate`) around the call.
154    rng: bool,
155    /// Return `Result<T, E>` to R without unwrapping -- R wrapper receives the result variant.
156    unwrap_in_r: bool,
157    /// Serialize the complete return value through `AsSerialize<T>`.
158    serialize: bool,
159    /// Explicit class-system return wrapping.
160    return_wrap: Option<crate::return_wrap::ReturnWrap>,
161    /// Parameter default values from `#[miniextendr(defaults(param = "value", ...))]`.
162    /// Keys are Rust names, values are R expressions used as default values;
163    /// the R wrapper keys them by formal (`Formals::rekey`).
164    param_defaults: std::collections::HashMap<String, String>,
165    /// The author's roxygen tags from the method's doc comment. The wrapper
166    /// generators forward the `@param` lines and the page tags (see
167    /// `r_wrappers::own_block_page`): a method-level `@rdname other` or
168    /// `@describeIn other ...` puts the method's own R wrapper block (S3
169    /// method, S4 `setMethod`, env/R6 namespace member, S7 shortcut, static
170    /// function) on that page instead of the type's shared `@rdname <Type>`
171    /// page, `@name` replaces the generated topic name, `@order` the generated
172    /// order, and `@inheritParams` / `@inherit` / `@inheritDotParams` fill the
173    /// arguments. Generics and consts stay on the type page.
174    doc_tags: Vec<String>,
175    /// When true, this method is excluded from C wrappers, R wrappers, and vtable shims.
176    /// The method is still kept in the emitted impl block (it's a real trait method).
177    skip: bool,
178    /// Override the R-facing method name. When set, the R wrapper uses this name
179    /// instead of the Rust method name (e.g., `next` -> `next_item` to avoid R reserved words).
180    r_name: Option<String>,
181    /// Strict output conversion: panic instead of lossy widening for i64/u64/isize/usize.
182    strict: bool,
183    /// Lifecycle specification for deprecation/experimental status.
184    lifecycle: Option<crate::lifecycle::LifecycleSpec>,
185    /// R code to inject at the very top of the wrapper body.
186    r_entry: Option<String>,
187    /// R code to inject after all checks, immediately before `.Call()`.
188    r_post_checks: Option<String>,
189    /// Register `on.exit()` cleanup code in the R wrapper.
190    r_on_exit: Option<crate::miniextendr_fn::ROnExit>,
191    /// Opt out of the per-class `<ClassName>_<method>` S7 fast-dispatch shortcut
192    /// (set via `#[miniextendr(s7(no_shortcut))]`). Only meaningful for S7 trait
193    /// impls; ignored by the other class systems.
194    no_shortcut: bool,
195    /// Existing S3/S4 generic and its receiver/formals contract.
196    generic: Option<String>,
197    receiver: Option<String>,
198    no_dispatch_dots: bool,
199    /// Per-parameter `#[miniextendr(match_arg(...))]` / `choices(...)` /
200    /// `several_ok` attributes, keyed by Rust parameter name. Mirrors
201    /// `MethodAttrs::per_param` on the inherent-impl path (`miniextendr_impl.rs`)
202    /// so trait methods get the same `match.arg()` validation prelude — see
203    /// `TraitMethodContext::match_arg_prelude`.
204    per_param: std::collections::HashMap<String, crate::miniextendr_fn::ParamAttrs>,
205    /// The method's own `preconditions` / `no_preconditions` (bare, #1017):
206    /// the type-derived R-side checks of the parameters that do not decide
207    /// for themselves (`preconditions(p)` / `no_preconditions(p)` land in
208    /// [`Self::per_param`]). `None` defers to the impl block.
209    preconditions: Option<bool>,
210}
211
212impl TraitMethod {
213    /// Returns the R-facing method name.
214    ///
215    /// Uses `r_name` override if set, otherwise falls back to the Rust identifier string.
216    fn r_method_name(&self) -> String {
217        self.r_name
218            .clone()
219            .unwrap_or_else(|| crate::naming::ident_name(&self.ident))
220    }
221
222    /// Generates the C wrapper function identifier: `C_{crate}_{Type}__{Trait}__{method}`.
223    ///
224    /// This is the symbol name registered with R via `R_CallMethodDef` for `.Call()` access.
225    /// Crate-prefixed for webR cross-package symbol uniqueness (#1273); shares one
226    /// formatter with `TraitConst` and with the string variant below so the shape
227    /// can't drift across the two types × two forms.
228    fn c_wrapper_ident(&self, type_ident: &syn::Ident, trait_name: &syn::Ident) -> syn::Ident {
229        crate::naming::trait_member_c_wrapper_ident(type_ident, trait_name, &self.ident)
230    }
231
232    /// Generates the C wrapper identifier as a `String`, for use in R-side `.Call()` generation.
233    ///
234    /// Prefer `c_wrapper_ident()` for Rust token generation; use this variant
235    /// when building R code strings (e.g., `".Call(C_crate_Type__Trait__method, ...)"`).
236    fn c_wrapper_ident_string(&self, type_ident: &syn::Ident, trait_name: &syn::Ident) -> String {
237        crate::naming::trait_member_c_wrapper_string(type_ident, trait_name, &self.ident)
238    }
239
240    /// Whether the R wrapper returns this method's value with `invisible()`
241    /// (#1213): set by an `Invisible<T>` return-type marker on the method.
242    /// Trait methods are visible by default, void ones included.
243    fn is_invisible(&self) -> bool {
244        self.invisible == Some(true)
245    }
246
247    /// Returns true if this method has no return type (returns unit `()`).
248    ///
249    /// Used to decide whether the R wrapper should hand back the receiver
250    /// (`x` / `self`) for void instance methods (pipe-friendly chaining).
251    fn returns_unit(&self) -> bool {
252        match &self.sig.output {
253            syn::ReturnType::Default => true,
254            syn::ReturnType::Type(_, ty) => {
255                #[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())
256            }
257        }
258    }
259
260    /// Returns true if this method returns `Self` (a factory/clone method).
261    ///
262    /// Mirrors [`crate::miniextendr_impl::ParsedMethod::returns_self`] on the
263    /// inherent-impl path. The C wrapper hands back a bare `ExternalPtr`
264    /// (`ReturnHandling::ExternalPtr`); the R wrapper must re-wrap it into a
265    /// classed object — see `TraitMethodContext::returns_self`.
266    fn returns_self(&self) -> bool {
267        #[allow(non_exhaustive_omitted_patterns)] match &self.sig.output {
    syn::ReturnType::Type(_, ty) if
        #[allow(non_exhaustive_omitted_patterns)] match ty.as_ref() {
            syn::Type::Path(p) if
                p.path.segments.last().map(|s|
                            s.ident == "Self").unwrap_or(false) => true,
            _ => false,
        } => true,
    _ => false,
}matches!(&self.sig.output, syn::ReturnType::Type(_, ty)
268            if matches!(ty.as_ref(), syn::Type::Path(p)
269                if p.path.segments.last().map(|s| s.ident == "Self").unwrap_or(false)))
270    }
271
272    /// Returns true if this method returns `Result<Self, E>`. Treated exactly
273    /// like a bare `Self` return on the R side (the C wrapper already raised on
274    /// `Err`, so `.val` is a bare `ExternalPtr`). Mirrors
275    /// [`crate::miniextendr_impl::ParsedMethod::returns_result_self`].
276    fn returns_result_self(&self) -> bool {
277        self.first_generic_arg_is_self("Result")
278    }
279
280    /// Returns true if this method returns `Option<Self>`. Treated exactly like
281    /// a bare `Self` return on the R side (the C wrapper already raised on
282    /// `None`). Mirrors
283    /// [`crate::miniextendr_impl::ParsedMethod::returns_option_self`].
284    fn returns_option_self(&self) -> bool {
285        self.first_generic_arg_is_self("Option")
286    }
287
288    /// Shared helper for [`Self::returns_result_self`] /
289    /// [`Self::returns_option_self`]: true when the return type is
290    /// `wrapper<Self, ..>` (e.g. `Result<Self, E>` / `Option<Self>`).
291    fn first_generic_arg_is_self(&self, wrapper: &str) -> bool {
292        let syn::ReturnType::Type(_, ty) = &self.sig.output else {
293            return false;
294        };
295        let syn::Type::Path(p) = ty.as_ref() else {
296            return false;
297        };
298        let Some(seg) = p.path.segments.last() else {
299            return false;
300        };
301        if seg.ident != wrapper {
302            return false;
303        }
304        let syn::PathArguments::AngleBracketed(ab) = &seg.arguments else {
305            return false;
306        };
307        #[allow(non_exhaustive_omitted_patterns)] match ab.args.first() {
    Some(syn::GenericArgument::Type(syn::Type::Path(ip))) if
        ip.path.segments.last().map(|s| s.ident == "Self").unwrap_or(false) =>
        true,
    _ => false,
}matches!(ab.args.first(), Some(syn::GenericArgument::Type(syn::Type::Path(ip)))
308            if ip.path.segments.last().map(|s| s.ident == "Self").unwrap_or(false))
309    }
310
311    /// Generates the `R_CallMethodDef` static identifier: `call_method_def_{Type}__{Trait}_{method}`.
312    ///
313    /// This constant holds the C function pointer and arity used by R's `.Call()` registration.
314    fn call_method_def_ident(
315        &self,
316        type_ident: &syn::Ident,
317        trait_name: &syn::Ident,
318    ) -> syn::Ident {
319        match ::quote::__private::IdentFragmentAdapter(&type_ident) {
    arg =>
        match ::quote::__private::IdentFragmentAdapter(&trait_name) {
            arg =>
                match ::quote::__private::IdentFragmentAdapter(&self.ident) {
                    arg =>
                        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                                        ::alloc::fmt::format(format_args!("call_method_def_{0}__{1}_{2}",
                                                arg, arg, arg))
                                    }),
                            ::quote::__private::Option::None.or(arg.span()).or(arg.span()).or(arg.span())),
                },
        },
}format_ident!(
320            "call_method_def_{}__{}_{}",
321            type_ident,
322            trait_name,
323            self.ident
324        )
325    }
326}
327
328/// Parsed associated constant from a trait impl block.
329///
330/// Trait constants are exposed to R as zero-argument `.Call()` wrappers
331/// that simply return the constant value.
332#[derive(#[automatically_derived]
impl ::core::fmt::Debug for TraitConst {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "TraitConst",
            "ident", &self.ident, "ty", &&self.ty)
    }
}Debug)]
333struct TraitConst {
334    /// Constant identifier (e.g., `MAX_SIZE`).
335    ident: syn::Ident,
336    /// Constant type (e.g., `i32`, `&str`). Used to determine SEXP conversion.
337    ty: syn::Type,
338}
339
340impl TraitConst {
341    /// Generates the C wrapper function identifier: `C_{crate}_{Type}__{Trait}__{CONST}`.
342    ///
343    /// This is the symbol name registered with R for `.Call()` access to the constant.
344    /// Crate-prefixed for webR cross-package symbol uniqueness (#1273); shares one
345    /// formatter with `TraitMethod` so the shape can't drift.
346    fn c_wrapper_ident(&self, type_ident: &syn::Ident, trait_name: &syn::Ident) -> syn::Ident {
347        crate::naming::trait_member_c_wrapper_ident(type_ident, trait_name, &self.ident)
348    }
349
350    /// Generates the C wrapper identifier as a `String`, for use in R-side `.Call()` generation.
351    fn c_wrapper_ident_string(&self, type_ident: &syn::Ident, trait_name: &syn::Ident) -> String {
352        crate::naming::trait_member_c_wrapper_string(type_ident, trait_name, &self.ident)
353    }
354
355    /// Generates the `R_CallMethodDef` static identifier: `call_method_def_{Type}__{Trait}_{CONST}`.
356    fn call_method_def_ident(
357        &self,
358        type_ident: &syn::Ident,
359        trait_name: &syn::Ident,
360    ) -> syn::Ident {
361        match ::quote::__private::IdentFragmentAdapter(&type_ident) {
    arg =>
        match ::quote::__private::IdentFragmentAdapter(&trait_name) {
            arg =>
                match ::quote::__private::IdentFragmentAdapter(&self.ident) {
                    arg =>
                        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                                        ::alloc::fmt::format(format_args!("call_method_def_{0}__{1}_{2}",
                                                arg, arg, arg))
                                    }),
                            ::quote::__private::Option::None.or(arg.span()).or(arg.span()).or(arg.span())),
                },
        },
}format_ident!(
362            "call_method_def_{}__{}_{}",
363            type_ident,
364            trait_name,
365            self.ident
366        )
367    }
368}
369
370/// Expand `#[miniextendr]` applied to a trait implementation.
371///
372/// # Arguments
373///
374/// * `attr` - Attribute arguments (currently unused)
375/// * `item` - The impl block token stream
376///
377/// # Returns
378///
379/// Expanded token stream containing:
380/// - Original impl block
381/// - Vtable static constant
382///
383/// # Errors
384///
385/// Returns a compile error if:
386/// - Not applied to a trait impl (`impl Trait for Type`)
387/// - Applied to an inherent impl (`impl Type`)
388pub fn expand_miniextendr_impl_trait(
389    attr: proc_macro::TokenStream,
390    item: proc_macro::TokenStream,
391) -> proc_macro::TokenStream {
392    // Parse class system from attribute (defaults to Env if empty)
393    let attr_tokens = TokenStream::from(attr.clone());
394    let impl_attrs: ImplAttrs = match ::syn::parse::<ImplAttrs>(attr) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(attr as ImplAttrs);
395    let impl_item = match ::syn::parse::<ItemImpl>(item) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(item as ItemImpl);
396
397    // Validate: must be a trait impl, not inherent impl
398    let (trait_path, concrete_type) = match extract_trait_and_type(&impl_item) {
399        Ok(result) => result,
400        Err(e) => return e.into_compile_error().into(),
401    };
402
403    // TPIE: empty impl body → expand via macro_rules! helper from the trait definition
404    if impl_item.items.is_empty() && !impl_attrs.blanket {
405        // Only the `@noRd` flag and the `@param` warnings are read here; the
406        // page rule is `generate_vtable_static`'s (#1818).
407        let raw_tags = crate::roxygen::roxygen_tags_from_attrs(
408            &impl_item.attrs,
409            crate::roxygen::RdPage::suppressed_if(impl_attrs.noexport && !impl_attrs.internal),
410        );
411        let (doc_tags, param_warnings) = crate::roxygen::strip_method_tags(
412            &raw_tags,
413            &concrete_type.to_token_stream().to_string(),
414            impl_item.impl_token.span,
415        );
416        let no_rd = crate::roxygen::has_roxygen_tag(&doc_tags, "noRd");
417        let mut output = generate_tpie_invocation(
418            &trait_path,
419            &concrete_type,
420            impl_attrs.class_system,
421            no_rd,
422            impl_attrs.internal,
423            impl_attrs.noexport,
424            // The trait's signatures carry no method or parameter spellings,
425            // so the impl block, the crate default and the feature decide;
426            // the `macro_rules!` contract carries the decision as a bool.
427            !crate::r_preconditions::resolve_item_type_checks(
428                None,
429                impl_attrs.preconditions,
430                crate::crate_config::preconditions_default(),
431                falsecfg!(feature = "no-preconditions-default"),
432            ),
433        );
434        output.extend(param_warnings);
435        return output.into();
436    }
437
438    // Ask the declaration for its R metadata before generating wrappers.
439    // Rust imports/re-exports the metadata macro beside the trait itself.
440    let mut macro_path = trait_path;
441    macro_path.segments.last_mut().unwrap().arguments = syn::PathArguments::None;
442    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&macro_path, &mut _s);
    ::quote::__private::push_bang(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_at(&mut _s);
            ::quote::__private::push_ident(&mut _s, "impl");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&attr_tokens, &mut _s);
                    _s
                });
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&impl_item, &mut _s);
                    _s
                });
            _s
        });
    _s
}quote::quote! {
443        #macro_path! { @impl { #attr_tokens } { #impl_item } }
444    }
445    .into()
446}
447
448/// Extract the trait path and concrete type from an impl block.
449///
450/// For `impl path::Trait for concrete::Type`, returns:
451/// - trait_path: `path::Trait`
452/// - concrete_type: `concrete::Type`
453fn extract_trait_and_type(impl_item: &ItemImpl) -> syn::Result<(syn::Path, syn::Type)> {
454    // Check for trait impl
455    let (_, trait_path, _) = impl_item.trait_.as_ref().ok_or_else(|| {
456        syn::Error::new_spanned(
457            impl_item,
458            "#[miniextendr] must be applied to a trait implementation (impl Trait for Type), \
459             not an inherent impl (impl Type)",
460        )
461    })?;
462
463    let concrete_type = (*impl_item.self_ty).clone();
464
465    Ok((trait_path.clone(), concrete_type))
466}
467
468// region: Sub-modules
469
470mod defaults;
471mod method_context;
472mod r_wrappers;
473mod vtable;
474
475use r_wrappers::TraitWrapperOpts;
476use r_wrappers::generate_trait_r_wrapper;
477use vtable::generate_trait_method_c_wrapper;
478use vtable::generate_vtable_static;
479
480/// Generate R function body lines that capture the `.Call()` result in `.val`,
481/// check for a tagged `rust_condition_value`, and return `.val`.
482/// `raise_default` is the raise helper's fallback call
483/// (`CallAttribution::raise_default`).
484fn trait_method_body_lines(call_expr: &str, indent: &str, raise_default: &str) -> Vec<String> {
485    let mut lines = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("{0}.val <- {1}", indent,
                                call_expr))
                    })]))vec![format!("{}.val <- {}", indent, call_expr)];
486    lines.extend(
487        crate::method_return_builder::condition_check_lines_with_default(indent, raise_default),
488    );
489    lines.push(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}.val", indent))
    })format!("{}.val", indent));
490    lines
491}
492
493/// Convert a type to a case-preserving identifier-safe name.
494///
495/// For non-generic types, the name is the last path segment with its spelling preserved:
496/// - `MyType` → `MyType`
497/// - `path::to::MyType` → `MyType`
498///
499/// For generic types, a 16-character lowercase hex suffix derived from a stable
500/// FNV-1a-64 hash of the full canonical token stream is appended:
501/// - `MyType<u32>` → `MyType_a1b2c3d4e5f60718`
502/// - `MyType<f64>` → `MyType_0102030405060708` (different hash → no collision)
503///
504/// This prevents vtable static name collisions when the same base type is
505/// monomorphised with different generic arguments in the same crate.
506///
507/// **Hash stability:** FNV-1a-64 with a fixed seed (offset basis = 0xcbf29ce484222325)
508/// is deterministic across builds, rustc versions, and platforms.  We deliberately
509/// avoid `std::collections::hash_map::DefaultHasher` and `RandomState` because both
510/// are explicitly unspecified and can change across releases.
511fn type_to_symbol_name(ty: &syn::Type) -> String {
512    /// FNV-1a 64-bit hash of a byte slice.
513    ///
514    /// Chosen for simplicity (≈10 lines, no new deps) and cross-build stability.
515    /// The FNV offset basis and prime are fixed constants defined in the FNV spec.
516    fn fnv1a_64(data: &[u8]) -> u64 {
517        const OFFSET_BASIS: u64 = 0xcbf29ce484222325;
518        const PRIME: u64 = 0x00000100000001b3;
519        let mut h = OFFSET_BASIS;
520        for &b in data {
521            h ^= b as u64;
522            h = h.wrapping_mul(PRIME);
523        }
524        h
525    }
526
527    match ty {
528        syn::Type::Path(type_path) => {
529            let last = type_path.path.segments.last();
530            let base = last
531                .map(|s| crate::naming::ident_name(&s.ident))
532                .unwrap_or_else(|| "UNKNOWN".to_string());
533
534            // Only append a hash suffix when the type actually carries generic arguments
535            // (e.g. `MyType<u32>`). Plain `MyType` keeps the clean `MyType` form.
536            let has_generics = last
537                .map(|s| !#[allow(non_exhaustive_omitted_patterns)] match s.arguments {
    syn::PathArguments::None => true,
    _ => false,
}matches!(s.arguments, syn::PathArguments::None))
538                .unwrap_or(false);
539
540            if has_generics {
541                // Canonical token string of the *full* type (including all generic args)
542                // so that `MyType<u32>` and `MyType<f64>` produce distinct hashes.
543                let token_str = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    _s
}quote::quote!(#ty).to_string();
544                let hash = fnv1a_64(token_str.as_bytes());
545                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}_{1:016x}", base, hash))
    })format!("{}_{:016x}", base, hash)
546            } else {
547                base
548            }
549        }
550        _ => "UNKNOWN".to_string(),
551    }
552}
553// endregion
554
555// region: TPIE: Trait-Provided Impl Expansion
556
557/// The declaration's ABI layout and tag, carried through its metadata macro.
558/// An impl must not reconstruct these from an imported or re-exported path.
559struct TraitAbiMetadata {
560    name: syn::Ident,
561    generic: bool,
562    tag: syn::LitStr,
563    fields: syn::FieldsNamed,
564}
565
566impl syn::parse::Parse for TraitAbiMetadata {
567    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
568        let keyword: syn::Ident = input.parse()?;
569        if keyword != "abi" {
570            return Err(syn::Error::new_spanned(
571                keyword,
572                "expected trait ABI metadata",
573            ));
574        }
575        Ok(Self {
576            name: input.parse()?,
577            generic: input.parse::<syn::LitBool>()?.value,
578            tag: input.parse()?,
579            fields: input.parse()?,
580        })
581    }
582}
583
584/// Populated impl plus declaration metadata, supplied by the trait's macro.
585/// Keep this as Rust syntax so method bodies, generics and author attributes
586/// pass through without a second bespoke representation.
587struct ImplWithMetadata {
588    attrs: ImplAttrs,
589    implementation: ItemImpl,
590    methods: Vec<syn::TraitItemFn>,
591    generics: syn::Generics,
592    abi: TraitAbiMetadata,
593}
594
595impl syn::parse::Parse for ImplWithMetadata {
596    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
597        input.parse::<::syn::token::Atsyn::Token![@]>()?;
598        input.parse::<::syn::token::Implsyn::Token![impl]>()?;
599        let attrs;
600        match ::syn::__private::parse_braces(&input) {
    ::syn::__private::Ok(braces) => {
        attrs = braces.content;
        _ = attrs;
        braces.token
    }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
};syn::braced!(attrs in input);
601        let attrs = attrs.parse()?;
602        let implementation;
603        match ::syn::__private::parse_braces(&input) {
    ::syn::__private::Ok(braces) => {
        implementation = braces.content;
        _ = implementation;
        braces.token
    }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
};syn::braced!(implementation in input);
604        let implementation = implementation.parse()?;
605        let metadata;
606        match ::syn::__private::parse_braces(&input) {
    ::syn::__private::Ok(braces) => {
        metadata = braces.content;
        _ = metadata;
        braces.token
    }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
};syn::braced!(metadata in input);
607        let mut methods = Vec::new();
608        while !metadata.is_empty() {
609            methods.push(metadata.parse()?);
610        }
611        let generics;
612        match ::syn::__private::parse_braces(&input) {
    ::syn::__private::Ok(braces) => {
        generics = braces.content;
        _ = generics;
        braces.token
    }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
};syn::braced!(generics in input);
613        Ok(Self {
614            attrs,
615            implementation,
616            methods,
617            generics: generics.parse()?,
618            abi: input.parse()?,
619        })
620    }
621}
622
623impl ImplWithMetadata {
624    fn expand(mut self) -> syn::Result<TokenStream> {
625        vtable::inherit_method_metadata(&mut self.implementation, &self.methods)?;
626        let (trait_path, concrete_type) = extract_trait_and_type(&self.implementation)?;
627        let defaults = defaults::inherited_methods(
628            &self.implementation,
629            &self.methods,
630            &self.generics,
631            &trait_path,
632        )?;
633        Ok(generate_vtable_static(
634            &self.implementation,
635            &trait_path,
636            &concrete_type,
637            &self.attrs,
638            &self.abi,
639            &self.methods,
640            &defaults,
641        ))
642    }
643}
644
645/// Input to the `__mx_trait_impl_expand!` proc macro.
646///
647/// TPIE (Trait-Provided Impl Expansion) allows empty `#[miniextendr] impl Trait for Type {}`
648/// blocks to auto-expand C/R wrappers using metadata embedded in a `macro_rules!` helper
649/// generated at the trait definition site.
650///
651/// Parsed from tokens like:
652/// ```text
653/// concrete_type = Point;
654/// trait_path = miniextendr_api::adapter_traits::RDebug;
655/// class_system = env;
656/// no_rd = false;
657/// internal = false;
658/// noexport = false;
659/// no_preconditions = false;
660/// abi RDebug false "<declaration fingerprint>" {
661///     pub debug_str: ::miniextendr_api::abi::mx_meth,
662///     pub debug_str_pretty: ::miniextendr_api::abi::mx_meth,
663/// }
664/// method { r_name = debug_str; fn debug_str(&self) -> String; }
665/// method { r_name = debug_str_pretty; fn debug_str_pretty(&self) -> String; }
666/// ```
667struct TpieInput {
668    /// The concrete type implementing the trait (e.g., `Point`).
669    concrete_type: syn::Type,
670    /// Fully qualified path to the trait (e.g., `miniextendr_api::adapter_traits::RDebug`).
671    trait_path: syn::Path,
672    /// Which R class system to generate wrappers for (env, r6, s3, s4, s7).
673    class_system: ClassSystem,
674    /// Whether the impl block has `@noRd`, suppressing roxygen documentation.
675    no_rd: bool,
676    /// Whether the impl block has `#[miniextendr(internal)]`, adding `@keywords internal`.
677    internal: bool,
678    /// Whether the impl block has `#[miniextendr(noexport)]`, suppressing `@export`.
679    noexport: bool,
680    /// Whether the impl block drops the R-side type checks: its
681    /// `ImplAttrs::preconditions`, the crate default and the
682    /// `no-preconditions-default` feature, resolved in the implementing crate
683    /// when the empty impl invoked the trait's `macro_rules!` helper.
684    no_preconditions: bool,
685    /// Method signatures and R-facing names from the trait definition.
686    methods: Vec<TpieMethod>,
687    abi: TraitAbiMetadata,
688}
689
690/// A single method entry in TPIE metadata.
691///
692/// Contains the R-facing name and the method signature as declared in the trait.
693struct TpieMethod {
694    /// The R-facing method name (may differ from the Rust ident via `r_name`).
695    r_name: String,
696    /// The method signature (parameters and return type) from the trait definition.
697    sig: syn::Signature,
698}
699
700impl syn::parse::Parse for TpieInput {
701    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
702        // concrete_type = Type;
703        let kw: syn::Ident = input.parse()?;
704        if kw != "concrete_type" {
705            return Err(syn::Error::new_spanned(
706                &kw,
707                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected \'concrete_type\', got \'{0}\'",
                kw))
    })format!("expected 'concrete_type', got '{}'", kw),
708            ));
709        }
710        input.parse::<::syn::token::Eqsyn::Token![=]>()?;
711        let concrete_type: syn::Type = input.parse()?;
712        input.parse::<::syn::token::Semisyn::Token![;]>()?;
713
714        // trait_path = some::Path;
715        let kw: syn::Ident = input.parse()?;
716        if kw != "trait_path" {
717            return Err(syn::Error::new_spanned(
718                &kw,
719                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected \'trait_path\', got \'{0}\'",
                kw))
    })format!("expected 'trait_path', got '{}'", kw),
720            ));
721        }
722        input.parse::<::syn::token::Eqsyn::Token![=]>()?;
723        let trait_path: syn::Path = input.parse()?;
724        input.parse::<::syn::token::Semisyn::Token![;]>()?;
725
726        // class_system = env;
727        let kw: syn::Ident = input.parse()?;
728        if kw != "class_system" {
729            return Err(syn::Error::new_spanned(
730                &kw,
731                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected \'class_system\', got \'{0}\'",
                kw))
    })format!("expected 'class_system', got '{}'", kw),
732            ));
733        }
734        input.parse::<::syn::token::Eqsyn::Token![=]>()?;
735        let cs_ident: syn::Ident = input.parse()?;
736        input.parse::<::syn::token::Semisyn::Token![;]>()?;
737
738        let class_system = ClassSystem::from_ident(&cs_ident).ok_or_else(|| {
739            syn::Error::new_spanned(&cs_ident, ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("unknown class system: {0}",
                cs_ident))
    })format!("unknown class system: {}", cs_ident))
740        })?;
741
742        // no_rd = true/false;
743        let kw: syn::Ident = input.parse()?;
744        if kw != "no_rd" {
745            return Err(syn::Error::new_spanned(
746                &kw,
747                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected \'no_rd\', got \'{0}\'",
                kw))
    })format!("expected 'no_rd', got '{}'", kw),
748            ));
749        }
750        input.parse::<::syn::token::Eqsyn::Token![=]>()?;
751        let no_rd_lit: syn::LitBool = input.parse()?;
752        input.parse::<::syn::token::Semisyn::Token![;]>()?;
753        let no_rd = no_rd_lit.value;
754
755        // internal = true/false;
756        let kw: syn::Ident = input.parse()?;
757        if kw != "internal" {
758            return Err(syn::Error::new_spanned(
759                &kw,
760                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected \'internal\', got \'{0}\'",
                kw))
    })format!("expected 'internal', got '{}'", kw),
761            ));
762        }
763        input.parse::<::syn::token::Eqsyn::Token![=]>()?;
764        let internal_lit: syn::LitBool = input.parse()?;
765        input.parse::<::syn::token::Semisyn::Token![;]>()?;
766        let internal = internal_lit.value;
767
768        // noexport = true/false;
769        let kw: syn::Ident = input.parse()?;
770        if kw != "noexport" {
771            return Err(syn::Error::new_spanned(
772                &kw,
773                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected \'noexport\', got \'{0}\'",
                kw))
    })format!("expected 'noexport', got '{}'", kw),
774            ));
775        }
776        input.parse::<::syn::token::Eqsyn::Token![=]>()?;
777        let noexport_lit: syn::LitBool = input.parse()?;
778        input.parse::<::syn::token::Semisyn::Token![;]>()?;
779        let noexport = noexport_lit.value;
780
781        // no_preconditions = true/false;
782        let kw: syn::Ident = input.parse()?;
783        if kw != "no_preconditions" {
784            return Err(syn::Error::new_spanned(
785                &kw,
786                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected \'no_preconditions\', got \'{0}\'",
                kw))
    })format!("expected 'no_preconditions', got '{}'", kw),
787            ));
788        }
789        input.parse::<::syn::token::Eqsyn::Token![=]>()?;
790        let no_preconditions_lit: syn::LitBool = input.parse()?;
791        input.parse::<::syn::token::Semisyn::Token![;]>()?;
792        let no_preconditions = no_preconditions_lit.value;
793
794        let abi = input.parse()?;
795
796        // method { ... } repeated
797        let mut methods = Vec::new();
798        while !input.is_empty() {
799            let kw: syn::Ident = input.parse()?;
800            if kw != "method" {
801                return Err(syn::Error::new_spanned(
802                    &kw,
803                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected \'method\', got \'{0}\'",
                kw))
    })format!("expected 'method', got '{}'", kw),
804                ));
805            }
806            let content;
807            match ::syn::__private::parse_braces(&input) {
    ::syn::__private::Ok(braces) => {
        content = braces.content;
        _ = content;
        braces.token
    }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
};syn::braced!(content in input);
808            methods.push(content.parse::<TpieMethod>()?);
809        }
810
811        Ok(TpieInput {
812            concrete_type,
813            trait_path,
814            class_system,
815            no_rd,
816            internal,
817            noexport,
818            no_preconditions,
819            methods,
820            abi,
821        })
822    }
823}
824
825impl syn::parse::Parse for TpieMethod {
826    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
827        // r_name = some_name;
828        let kw: syn::Ident = input.parse()?;
829        if kw != "r_name" {
830            return Err(syn::Error::new_spanned(
831                &kw,
832                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("expected \'r_name\', got \'{0}\'",
                kw))
    })format!("expected 'r_name', got '{}'", kw),
833            ));
834        }
835        input.parse::<::syn::token::Eqsyn::Token![=]>()?;
836        let r_name_ident: syn::Ident = input.parse()?;
837        input.parse::<::syn::token::Semisyn::Token![;]>()?;
838
839        // fn method_name(...) -> ReturnType;
840        let sig: syn::Signature = input.parse()?;
841        input.parse::<::syn::token::Semisyn::Token![;]>()?;
842
843        Ok(TpieMethod {
844            r_name: crate::naming::ident_name(&r_name_ident),
845            sig,
846        })
847    }
848}
849
850/// Rewrite `Self` → concrete type in a method signature's parameters.
851///
852/// `&Self` params are left as-is because `generate_trait_method_c_wrapper`
853/// detects them via `type_inspect::self_ref_mutability` and generates `ExternalPtr<T>` extraction.
854///
855/// The return type keeps `Self`, as in a trait impl with method bodies: the
856/// C wrapper hands a `Self` / `Option<Self>` / `Result<Self, E>` back as an
857/// `ExternalPtr` without naming the type, and the R wrapper re-wraps exactly
858/// those returns into an object of the class (`TraitMethod::returns_self` and
859/// its siblings look for `Self`).
860fn rewrite_self_in_params(sig: &mut syn::Signature, concrete_type: &syn::Type) {
861    for input in &mut sig.inputs {
862        if let syn::FnArg::Typed(pt) = input {
863            // Don't rewrite &Self — C wrapper generator handles it specially
864            if crate::type_inspect::self_ref_mutability(&pt.ty).is_some() {
865                continue;
866            }
867            let rewritten = rewrite_self_type(&pt.ty, concrete_type);
868            *pt.ty = rewritten;
869        }
870    }
871}
872
873/// Recursively replace `Self` with the concrete type in a type tree.
874fn rewrite_self_type(ty: &syn::Type, concrete_type: &syn::Type) -> syn::Type {
875    match ty {
876        syn::Type::Path(tp) => {
877            if tp.path.is_ident("Self") {
878                return concrete_type.clone();
879            }
880            let mut new_tp = tp.clone();
881            for seg in &mut new_tp.path.segments {
882                if let syn::PathArguments::AngleBracketed(args) = &mut seg.arguments {
883                    for arg in &mut args.args {
884                        if let syn::GenericArgument::Type(inner) = arg {
885                            *inner = rewrite_self_type(inner, concrete_type);
886                        }
887                    }
888                }
889            }
890            syn::Type::Path(new_tp)
891        }
892        syn::Type::Reference(r) => {
893            let mut new_r = r.clone();
894            new_r.elem = Box::new(rewrite_self_type(&r.elem, concrete_type));
895            syn::Type::Reference(new_r)
896        }
897        syn::Type::Tuple(t) => {
898            let mut new_t = t.clone();
899            for elem in &mut new_t.elems {
900                *elem = rewrite_self_type(elem, concrete_type);
901            }
902            syn::Type::Tuple(new_t)
903        }
904        _ => ty.clone(),
905    }
906}
907
908/// Unwrap invisible Group tokens from `macro_rules!` `$t:ty` captures.
909///
910/// When a type passes through a `macro_rules!` pattern like `$concrete_type:ty`,
911/// the compiler wraps it in a `Group` with invisible delimiters. This function
912/// recursively unwraps those groups to get the underlying type.
913fn unwrap_group_type(ty: &syn::Type) -> syn::Type {
914    match ty {
915        syn::Type::Group(g) => unwrap_group_type(&g.elem),
916        _ => ty.clone(),
917    }
918}
919
920/// Entry point for the `__mx_trait_impl_expand!` proc macro.
921///
922/// Parses TPIE metadata tokens and generates C wrappers, R wrappers,
923/// and call defs — the same outputs as a manual trait impl with method bodies.
924pub fn expand_tpie(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
925    if #[allow(non_exhaustive_omitted_patterns)] match input.clone().into_iter().next()
    {
    Some(proc_macro::TokenTree::Punct(p)) if p.as_char() == '@' => true,
    _ => false,
}matches!(input.clone().into_iter().next(), Some(proc_macro::TokenTree::Punct(p)) if p.as_char() == '@')
926    {
927        let metadata = match ::syn::parse::<ImplWithMetadata>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as ImplWithMetadata);
928        return metadata
929            .expand()
930            .unwrap_or_else(syn::Error::into_compile_error)
931            .into();
932    }
933    let tpie_input = match ::syn::parse::<TpieInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}syn::parse_macro_input!(input as TpieInput);
934
935    // Unwrap Group tokens (macro_rules! wraps $concrete_type:ty and $trait_path:path
936    // in invisible groups)
937    let concrete_type = unwrap_group_type(&tpie_input.concrete_type);
938    let trait_path = &tpie_input.trait_path;
939    let class_system = tpie_input.class_system;
940
941    let trait_name = &tpie_input.abi.name;
942
943    let type_ident = match &concrete_type {
944        syn::Type::Path(tp) => tp
945            .path
946            .segments
947            .last()
948            .map(|s| s.ident.clone())
949            .unwrap_or_else(|| ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("Unknown"))
            }), ::quote::__private::Option::None)format_ident!("Unknown")),
950        _ => ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("Unknown"))
            }), ::quote::__private::Option::None)format_ident!("Unknown"),
951    };
952
953    // Convert TpieMethod → TraitMethod, rewriting Self → ConcreteType
954    let methods: Vec<TraitMethod> = tpie_input
955        .methods
956        .iter()
957        .map(|tm| {
958            let mut sig = tm.sig.clone();
959            rewrite_self_in_params(&mut sig, &concrete_type);
960
961            let (has_self, is_mut) = sig.inputs.first().map_or((false, false), |arg| {
962                if let syn::FnArg::Receiver(_) = arg {
963                    (true, crate::trait_receiver::is_mut(&sig))
964                } else {
965                    (false, false)
966                }
967            });
968
969            // Peel a return-visibility marker (#1213) so the return-shape
970            // predicates and the C wrapper see the inner type.
971            let (invisible, sig) = {
972                let (marker, output) = crate::type_inspect::peel_return_visibility(&sig.output);
973                let mut sig = sig;
974                sig.output = output;
975                (marker, sig)
976            };
977            TraitMethod {
978                inherited_signature: true,
979                ident: sig.ident.clone(),
980                sig,
981                invisible,
982                has_self,
983                is_mut,
984                worker: falsecfg!(feature = "worker-default"),
985                unsafe_main_thread: false,
986                coerce: false,
987                check_interrupt: false,
988                rng: false,
989                unwrap_in_r: false,
990                serialize: false,
991                return_wrap: None,
992                param_defaults: Default::default(),
993                doc_tags: ::alloc::vec::Vec::new()vec![],
994                skip: false,
995                strict: false,
996                lifecycle: None,
997                r_entry: None,
998                r_post_checks: None,
999                r_on_exit: None,
1000                no_shortcut: false,
1001                generic: None,
1002                receiver: None,
1003                no_dispatch_dots: false,
1004                per_param: Default::default(),
1005                preconditions: None,
1006                r_name: if tm.sig.ident == tm.r_name {
1007                    None // r_name matches ident → no override
1008                } else {
1009                    Some(tm.r_name.clone())
1010                },
1011            }
1012        })
1013        .collect();
1014
1015    // Generate C wrappers
1016    let c_wrappers: Vec<TokenStream> = methods
1017        .iter()
1018        .map(|m| generate_trait_method_c_wrapper(m, &type_ident, trait_name, trait_path))
1019        .collect();
1020
1021    // Generate R wrappers
1022    let r_wrapper_string = match generate_trait_r_wrapper(
1023        &type_ident,
1024        trait_name,
1025        &methods,
1026        &[], // no consts in TPIE
1027        TraitWrapperOpts {
1028            class_system,
1029            class_has_no_rd: tpie_input.no_rd,
1030            internal: tpie_input.internal,
1031            noexport: tpie_input.noexport,
1032            impl_preconditions: Some(!tpie_input.no_preconditions),
1033        },
1034    ) {
1035        Ok(s) => s,
1036        Err(e) => return e.into_compile_error().into(),
1037    };
1038
1039    // Generate const names (same pattern as generate_vtable_static)
1040    let type_name_str = type_to_symbol_name(&concrete_type);
1041    let r_wrappers_const = crate::naming::trait_impl_wrappers_ident(&type_name_str, trait_name);
1042
1043    // Generate trait dispatch entry name
1044    let dispatch_entry_name = crate::naming::trait_dispatch_ident(trait_name, &type_name_str);
1045
1046    let trait_tag = &tpie_input.abi.tag;
1047
1048    // Build vtable static name (same as generate_vtable_static — routed through
1049    // the shared naming.rs helper so the three reconstruction sites can't drift, #1273)
1050    let vtable_static_name = crate::naming::vtable_static_ident(trait_name, &type_name_str);
1051
1052    let vtable = match vtable::generate_impl_vtable(
1053        &tpie_input.abi,
1054        &concrete_type,
1055        trait_path,
1056        &methods,
1057        &[],
1058    ) {
1059        Ok(tokens) => tokens,
1060        Err(error) => return error.into_compile_error().into(),
1061    };
1062
1063    // Format R wrapper as raw string literal
1064    let r_wrapper_str = crate::r_wrapper_raw_literal(
1065        &r_wrapper_string,
1066        &crate::r_shadowing::formal_names(
1067            methods.iter().map(|m| (&m.sig.inputs, Default::default())),
1068        ),
1069    );
1070    let source_start = type_ident.span().start();
1071    let source_line_lit = syn::LitInt::new(&source_start.line.to_string(), type_ident.span());
1072    let source_col_lit =
1073        syn::LitInt::new(&(source_start.column + 1).to_string(), type_ident.span());
1074
1075    let expanded = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&vtable, &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_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_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! {
1076        #vtable
1077        // C wrappers and call method defs for trait methods
1078        #(#c_wrappers)*
1079
1080        // R wrapper registration via distributed slice
1081        #[doc(hidden)]
1082        #[cfg_attr(not(target_arch = "wasm32"), ::miniextendr_api::linkme::distributed_slice(::miniextendr_api::registry::MX_R_WRAPPERS), linkme(crate = ::miniextendr_api::linkme))]
1083        #[allow(non_upper_case_globals)]
1084        static #r_wrappers_const: ::miniextendr_api::registry::RWrapperEntry =
1085            ::miniextendr_api::registry::RWrapperEntry {
1086                priority: ::miniextendr_api::registry::RWrapperPriority::TraitImpl,
1087                source_file: ::core::file!(),
1088                source_line: #source_line_lit,
1089                content: ::core::concat!(
1090                    "# Generated from Rust impl `",
1091                    ::core::stringify!(#trait_name),
1092                    "` for `",
1093                    ::core::stringify!(#type_ident),
1094                    "` (",
1095                    ::core::file!(),
1096                    ":",
1097                    #source_line_lit,
1098                    ":",
1099                    #source_col_lit,
1100                    ")",
1101                    #r_wrapper_str
1102                ),
1103            };
1104
1105        // Trait dispatch entry for universal_query
1106        #[doc(hidden)]
1107        #[cfg_attr(not(target_arch = "wasm32"), ::miniextendr_api::linkme::distributed_slice(::miniextendr_api::registry::MX_TRAIT_DISPATCH), linkme(crate = ::miniextendr_api::linkme))]
1108        #[allow(non_upper_case_globals)]
1109        static #dispatch_entry_name: ::miniextendr_api::registry::TraitDispatchEntry =
1110            ::miniextendr_api::registry::TraitDispatchEntry {
1111                concrete_tag: ::miniextendr_api::abi::mx_tag_from_path(
1112                    ::core::concat!(::core::module_path!(), "::", ::core::stringify!(#type_ident))
1113                ),
1114                trait_tag: ::miniextendr_api::abi::mx_tag_from_path(#trait_tag),
1115                vtable: unsafe {
1116                    ::std::ptr::from_ref(&#vtable_static_name).cast::<::std::os::raw::c_void>()
1117                },
1118                vtable_symbol: ::core::stringify!(#vtable_static_name),
1119            };
1120    };
1121
1122    expanded.into()
1123}
1124
1125/// Request declaration metadata for an empty trait impl.
1126///
1127/// When `#[miniextendr] impl Trait for Type {}` has no method bodies, this
1128/// delegates to the trait's metadata macro
1129/// (generated at the trait definition site) to expand C/R wrappers.
1130fn generate_tpie_invocation(
1131    trait_path: &syn::Path,
1132    concrete_type: &syn::Type,
1133    class_system: ClassSystem,
1134    class_has_no_rd: bool,
1135    internal: bool,
1136    noexport: bool,
1137    no_preconditions: bool,
1138) -> TokenStream {
1139    // Resolve metadata through the same path as the trait. The declaration
1140    // exports both in separate Rust namespaces.
1141    let mut macro_path = trait_path.clone();
1142    macro_path.segments.last_mut().unwrap().arguments = syn::PathArguments::None;
1143    let class_system_ident = class_system.to_ident();
1144    let no_rd_ident = if class_has_no_rd {
1145        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("true"))
            }), ::quote::__private::Option::None)format_ident!("true")
1146    } else {
1147        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("false"))
            }), ::quote::__private::Option::None)format_ident!("false")
1148    };
1149    let internal_ident = if internal {
1150        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("true"))
            }), ::quote::__private::Option::None)format_ident!("true")
1151    } else {
1152        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("false"))
            }), ::quote::__private::Option::None)format_ident!("false")
1153    };
1154    let noexport_ident = if noexport {
1155        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("true"))
            }), ::quote::__private::Option::None)format_ident!("true")
1156    } else {
1157        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("false"))
            }), ::quote::__private::Option::None)format_ident!("false")
1158    };
1159    let no_preconditions_ident = if no_preconditions {
1160        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("true"))
            }), ::quote::__private::Option::None)format_ident!("true")
1161    } else {
1162        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("false"))
            }), ::quote::__private::Option::None)format_ident!("false")
1163    };
1164
1165    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&macro_path, &mut _s);
    ::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);
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&trait_path, &mut _s);
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&class_system_ident, &mut _s);
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&no_rd_ident, &mut _s);
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&internal_ident, &mut _s);
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&noexport_ident, &mut _s);
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&no_preconditions_ident, &mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    _s
}quote::quote! {
1166        #macro_path !(#concrete_type, #trait_path, #class_system_ident, #no_rd_ident, #internal_ident, #noexport_ident, #no_preconditions_ident);
1167    }
1168}
1169
1170#[cfg(test)]
1171mod tests;
1172// endregion