Skip to main content

miniextendr_macros/
trait_self_ref.rs

1//! Shared pre-conversion alias checks and Self-reference lowering for trait shims.
2
3use proc_macro2::TokenStream;
4use quote::{format_ident, quote};
5
6/// Check native arguments before conversion can create references. Arity
7/// must already be checked, and the caller must be inside the shim's R unwind
8/// boundary. `&Self` uses ExternalPtr handles and its separate address check.
9pub(crate) fn native_borrow_checks<'a>(
10    args: impl IntoIterator<Item = (usize, String, &'a syn::Type)>,
11    argv: &syn::Ident,
12    targets: Option<&[syn::Ident]>,
13) -> TokenStream {
14    let mut values = Vec::new();
15    let mut metadata = Vec::new();
16    for (index, name, ty) in args {
17        if crate::type_inspect::self_ref_mutability(ty).is_some() {
18            continue;
19        }
20        values.push({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_star(&mut _s);
            ::quote::ToTokens::to_tokens(&argv, &mut _s);
            ::quote::__private::push_dot(&mut _s);
            ::quote::__private::push_ident(&mut _s, "add");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&index, &mut _s);
                    _s
                });
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&name, &mut _s);
            _s
        });
    _s
}quote! { (*#argv.add(#index), #name) });
21        if let Some(targets) = targets {
22            let target = &targets[index];
23            metadata.push(
24                if crate::type_inspect::is_rooted_view(ty)
25                    || crate::type_inspect::is_rooted_reference(ty)
26                {
27                    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&target, &mut _s);
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "rooted_native_borrow");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    _s
}quote! { #target.rooted_native_borrow() }
28                } else {
29                    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&target, &mut _s);
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "native_borrow");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    _s
}quote! { #target.native_borrow() }
30                },
31            );
32            continue;
33        }
34        metadata.push(
35            if crate::type_inspect::is_rooted_view(ty)
36                || crate::type_inspect::is_rooted_reference(ty)
37            {
38                let ty = crate::type_inspect::erase_lifetimes(ty);
39                {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "TryFromRoot");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_lifetime(&mut _s, "\'_");
    ::quote::__private::push_shr(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "NATIVE_BORROW");
    _s
}quote! { <#ty as ::miniextendr_api::TryFromRoot<'_>>::NATIVE_BORROW }
40            } else {
41                {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "TryFromSexp");
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "NATIVE_BORROW");
    _s
}quote! { <#ty as ::miniextendr_api::TryFromSexp>::NATIVE_BORROW }
42            },
43        );
44    }
45    if values.is_empty() {
46        return TokenStream::new();
47    }
48    let borrows = syn::Ident::new(
49        "__miniextendr_native_borrows",
50        proc_macro2::Span::mixed_site(),
51    );
52    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "let");
            ::quote::ToTokens::to_tokens(&borrows, &mut _s);
            ::quote::__private::push_eq(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    {
                        use ::quote::__private::ext::*;
                        let mut _first = true;
                        let has_iter = ::quote::__private::HasIterator::<false>;
                        #[allow(unused_mut)]
                        let (mut metadata, i) = metadata.quote_into_iter();
                        let has_iter = has_iter | i;
                        <_ as
                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                        while true {
                            let metadata =
                                match metadata.next() {
                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                    None => break,
                                };
                            if !_first { ::quote::__private::push_comma(&mut _s); }
                            _first = false;
                            ::quote::ToTokens::to_tokens(&metadata, &mut _s);
                        }
                    }
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            ::quote::__private::push_ident(&mut _s, "if");
            ::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, "from_r");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "borrow");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "needs_check");
            ::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(&borrows, &mut _s);
                    _s
                });
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "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, "from_r");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "borrow");
                            ::quote::__private::push_colon2(&mut _s);
                            ::quote::__private::push_ident(&mut _s,
                                "assert_no_aliases");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Bracket,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            {
                                                use ::quote::__private::ext::*;
                                                let mut _first = true;
                                                let has_iter = ::quote::__private::HasIterator::<false>;
                                                #[allow(unused_mut)]
                                                let (mut values, i) = values.quote_into_iter();
                                                let has_iter = has_iter | i;
                                                <_ as
                                                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                                while true {
                                                    let values =
                                                        match values.next() {
                                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                                            None => break,
                                                        };
                                                    if !_first { ::quote::__private::push_comma(&mut _s); }
                                                    _first = false;
                                                    ::quote::ToTokens::to_tokens(&values, &mut _s);
                                                }
                                            }
                                            _s
                                        });
                                    ::quote::__private::push_comma(&mut _s);
                                    ::quote::ToTokens::to_tokens(&borrows, &mut _s);
                                    ::quote::__private::push_comma(&mut _s);
                                    _s
                                });
                            ::quote::__private::push_semi(&mut _s);
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote! {{
53        let #borrows = [#(#metadata),*];
54        if ::miniextendr_api::from_r::borrow::needs_check(&#borrows) {
55            // SAFETY: arity was checked before reading argv; its arguments
56            // remain rooted on R's thread inside the shim's unwind boundary.
57            unsafe {
58                ::miniextendr_api::from_r::borrow::assert_no_aliases(
59                    [#(#values),*], #borrows,
60                );
61            }
62        }
63    }}
64}
65
66/// Convert one shim argument with the C wrapper's structured error arm.
67/// Arity and native alias checks must run first. Self handles are borrowed
68/// later, after their address checks; rooted views retain a local scope until
69/// the method's return has been converted.
70pub(crate) fn argument(
71    name: &syn::Ident,
72    diagnostic_name: &str,
73    ty: &syn::Type,
74    impl_ty: &impl quote::ToTokens,
75    index: usize,
76    argv: &syn::Ident,
77    target: Option<&syn::Ident>,
78) -> TokenStream {
79    let target = target.filter(|_| crate::type_inspect::self_ref_mutability(ty).is_none());
80    let input = {
    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_star(&mut _s);
            ::quote::ToTokens::to_tokens(&argv, &mut _s);
            ::quote::__private::push_dot(&mut _s);
            ::quote::__private::push_ident(&mut _s, "add");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&index, &mut _s);
                    _s
                });
            _s
        });
    _s
}quote! { unsafe { *#argv.add(#index) } };
81    let r_name = crate::r_wrapper_builder::normalize_r_arg_string(diagnostic_name);
82    let (setup, binding, mut read_ty, mut conversion, rooted) = if let Some(mutable) =
83        crate::type_inspect::self_ref_mutability(ty)
84    {
85        let handle = handle_ident(name);
86        let mutability = mutable.then(|| {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "mut");
    _s
}quote!(mut));
87        let read_ty: syn::Type = ::syn::__private::parse_quote({
        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_lt(&mut _s);
        ::quote::ToTokens::to_tokens(&impl_ty, &mut _s);
        ::quote::__private::push_gt(&mut _s);
        _s
    })syn::parse_quote!(::miniextendr_api::ExternalPtr<#impl_ty>);
88        let conversion = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&read_ty, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "TryFromSexp");
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "try_from_sexp");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&input, &mut _s);
            _s
        });
    _s
}quote! {
89            <#read_ty as ::miniextendr_api::TryFromSexp>::try_from_sexp(#input)
90        };
91        (
92            TokenStream::new(),
93            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&mutability, &mut _s);
    ::quote::ToTokens::to_tokens(&handle, &mut _s);
    _s
}quote!(#mutability #handle),
94            read_ty,
95            conversion,
96            false,
97        )
98    } else if crate::type_inspect::is_rooted_view(ty)
99        || crate::type_inspect::is_rooted_reference(ty)
100    {
101        if let Some(stmt) = crate::type_inspect::static_rooted_view_binding(name, ty) {
102            return stmt;
103        }
104        let slot = match ::quote::__private::IdentFragmentAdapter(&crate::naming::unraw(name)) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("__mx_root_{0}", arg))
                    }),
            ::quote::__private::Option::Some::<::quote::__private::Span>(proc_macro2::Span::mixed_site())),
}format_ident!(
105            "__mx_root_{}",
106            crate::naming::unraw(name),
107            span = proc_macro2::Span::mixed_site()
108        );
109        let read_ty = crate::type_inspect::erase_lifetimes(ty);
110        let conversion = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&read_ty, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "TryFromRoot");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_lifetime(&mut _s, "\'_");
    ::quote::__private::push_shr(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "try_from_root");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "unsafe");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&slot, &mut _s);
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "rooted");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_star(&mut _s);
                            ::quote::ToTokens::to_tokens(&argv, &mut _s);
                            ::quote::__private::push_dot(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "add");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::ToTokens::to_tokens(&index, &mut _s);
                                    _s
                                });
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote! {
111            <#read_ty as ::miniextendr_api::TryFromRoot<'_>>::try_from_root(unsafe {
112                #slot.rooted(*#argv.add(#index))
113            })
114        };
115        (
116            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&slot, &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, "gc_protect");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "ProtectScope");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "new");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                ::quote::__private::TokenStream::new());
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    _s
}quote! { let #slot = unsafe { ::miniextendr_api::gc_protect::ProtectScope::new() }; },
117            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    _s
}quote!(#name),
118            read_ty,
119            conversion,
120            true,
121        )
122    } else {
123        let conversion = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "miniextendr_api");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "TryFromSexp");
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "try_from_sexp");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&input, &mut _s);
            _s
        });
    _s
}quote! {
124            <#ty as ::miniextendr_api::TryFromSexp>::try_from_sexp(#input)
125        };
126        (
127            TokenStream::new(),
128            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    _s
}quote!(#name),
129            ty.clone(),
130            conversion,
131            false,
132        )
133    };
134    if let Some(target) = target {
135        read_ty = ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_underscore(&mut _s);
        _s
    })syn::parse_quote!(_);
136        conversion = if rooted {
137            let slot = match ::quote::__private::IdentFragmentAdapter(&crate::naming::unraw(name)) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("__mx_root_{0}", arg))
                    }),
            ::quote::__private::Option::Some::<::quote::__private::Span>(proc_macro2::Span::mixed_site())),
}format_ident!(
138                "__mx_root_{}",
139                crate::naming::unraw(name),
140                span = proc_macro2::Span::mixed_site()
141            );
142            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&target, &mut _s);
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "rooted");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "unsafe");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&slot, &mut _s);
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "rooted");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_star(&mut _s);
                            ::quote::ToTokens::to_tokens(&argv, &mut _s);
                            ::quote::__private::push_dot(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "add");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::ToTokens::to_tokens(&index, &mut _s);
                                    _s
                                });
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote! { #target.rooted(unsafe { #slot.rooted(*#argv.add(#index)) }) }
143        } else {
144            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&target, &mut _s);
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "convert");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&input, &mut _s);
            _s
        });
    _s
}quote! { #target.convert(#input) }
145        };
146    }
147    let err_arm = crate::rust_conversion_builder::shim_conversion_err_arm(
148        &r_name,
149        ty,
150        (!rooted).then_some(&read_ty),
151        target.filter(|_| !rooted),
152    );
153    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&setup, &mut _s);
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&binding, &mut _s);
    ::quote::__private::push_colon(&mut _s);
    ::quote::ToTokens::to_tokens(&read_ty, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_ident(&mut _s, "match");
    ::quote::ToTokens::to_tokens(&conversion, &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, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "result");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Ok");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "value");
                    _s
                });
            ::quote::__private::push_fat_arrow(&mut _s);
            ::quote::__private::push_ident(&mut _s, "value");
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&err_arm, &mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    _s
}quote! {
154        #setup
155        let #binding: #read_ty = match #conversion {
156            ::core::result::Result::Ok(value) => value,
157            #err_arm
158        };
159    }
160}
161
162/// A shim's receiver address for [`alias_checks`]: the value inside the
163/// receiver's `RefCell`, comparable with `ExternalPtr::as_ptr` of a `&Self`
164/// argument.
165pub(crate) fn cell_address(cell: &syn::Ident) -> TokenStream {
166    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&cell, &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_const");
    ::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());
    _s
}quote! { #cell.as_ptr().cast_const().cast::<::core::ffi::c_void>() }
167}
168
169/// Borrow a shim's receiver from its `RefCell` for the rest of the shim:
170/// shared for `&self`, exclusive for `&mut self`. A call still running on the
171/// same object makes this raise the `miniextendr_borrow_conflict` error.
172pub(crate) fn receiver_borrow(
173    cell: &syn::Ident,
174    guard: &syn::Ident,
175    self_ref: &syn::Ident,
176    mutable: bool,
177) -> TokenStream {
178    if mutable {
179        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::__private::push_ident(&mut _s, "mut");
    ::quote::ToTokens::to_tokens(&guard, &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, "borrow_value_mut");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&cell, &mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&self_ref, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_and(&mut _s);
    ::quote::__private::push_ident(&mut _s, "mut");
    ::quote::__private::push_star(&mut _s);
    ::quote::ToTokens::to_tokens(&guard, &mut _s);
    ::quote::__private::push_semi(&mut _s);
    _s
}quote! {
180            let mut #guard = ::miniextendr_api::externalptr::borrow_value_mut(#cell);
181            let #self_ref = &mut *#guard;
182        }
183    } else {
184        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&guard, &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, "borrow_value");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&cell, &mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&self_ref, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_and(&mut _s);
    ::quote::__private::push_star(&mut _s);
    ::quote::ToTokens::to_tokens(&guard, &mut _s);
    ::quote::__private::push_semi(&mut _s);
    _s
}quote! {
185            let #guard = ::miniextendr_api::externalptr::borrow_value(#cell);
186            let #self_ref = &*#guard;
187        }
188    }
189}
190
191pub(crate) fn handle_ident(name: &syn::Ident) -> syn::Ident {
192    match ::quote::__private::IdentFragmentAdapter(&crate::naming::unraw(name)) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("__extptr_{0}", arg))
                    }),
            ::quote::__private::Option::Some::<::quote::__private::Span>(proc_macro2::Span::mixed_site())),
}format_ident!(
193        "__extptr_{}",
194        crate::naming::unraw(name),
195        span = proc_macro2::Span::mixed_site()
196    )
197}
198
199/// Borrow a `&Self` argument's value for the rest of the shim, exclusively
200/// for `&mut Self`, as a C wrapper borrows a `&T` / `&mut T` argument
201/// (#2106): a call still running on the object makes the borrow raise the
202/// `miniextendr_borrow_conflict` error. The handle [`argument`] made roots the
203/// pointer of a class-wrapped argument. Runs after [`alias_checks`].
204pub(crate) fn borrow(name: &syn::Ident, mutable: bool) -> TokenStream {
205    let handle = handle_ident(name);
206    let guard = match ::quote::__private::IdentFragmentAdapter(&crate::naming::unraw(name)) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("__mx_guard_{0}", arg))
                    }),
            ::quote::__private::Option::Some::<::quote::__private::Span>(proc_macro2::Span::mixed_site())),
}format_ident!(
207        "__mx_guard_{}",
208        crate::naming::unraw(name),
209        span = proc_macro2::Span::mixed_site()
210    );
211    if mutable {
212        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::__private::push_ident(&mut _s, "mut");
    ::quote::ToTokens::to_tokens(&guard, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::ToTokens::to_tokens(&handle, &mut _s);
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "borrow_mut");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    ::quote::__private::push_semi(&mut _s);
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_and(&mut _s);
    ::quote::__private::push_ident(&mut _s, "mut");
    ::quote::__private::push_star(&mut _s);
    ::quote::ToTokens::to_tokens(&guard, &mut _s);
    ::quote::__private::push_semi(&mut _s);
    _s
}quote! {
213            let mut #guard = #handle.borrow_mut();
214            let #name = &mut *#guard;
215        }
216    } else {
217        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&guard, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::ToTokens::to_tokens(&handle, &mut _s);
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "borrow");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    ::quote::__private::push_semi(&mut _s);
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_and(&mut _s);
    ::quote::__private::push_star(&mut _s);
    ::quote::ToTokens::to_tokens(&guard, &mut _s);
    ::quote::__private::push_semi(&mut _s);
    _s
}quote! {
218            let #guard = #handle.borrow();
219            let #name = &*#guard;
220        }
221    }
222}
223
224/// Reject overlapping Self arguments before any of their references exist.
225/// Shared/shared pairs are allowed. Reentrant access remains a separate concern.
226pub(crate) fn alias_checks(
227    ty: &impl quote::ToTokens,
228    handles: &[(syn::Ident, bool, String)],
229    receiver: Option<(TokenStream, bool)>,
230) -> TokenStream {
231    let mut pointers: Vec<_> = handles
232        .iter()
233        .map(|(handle, mutable, name)| {
234            (
235                {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&handle, &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());
    _s
}quote!(#handle.as_ptr().cast::<::core::ffi::c_void>()),
236                *mutable,
237                name.clone(),
238            )
239        })
240        .collect();
241    if let Some((pointer, mutable)) = receiver {
242        pointers.insert(0, (pointer, mutable, "self".to_owned()));
243    }
244    let mut checks = Vec::new();
245    for (index, (left, left_mut, left_name)) in pointers.iter().enumerate() {
246        for (right, right_mut, right_name) in &pointers[index + 1..] {
247            if *left_mut || *right_mut {
248                let message = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("trait arguments \'{0}\' and \'{1}\' alias the same object while one is mutable",
                left_name, right_name))
    })format!(
249                    "trait arguments '{left_name}' and '{right_name}' alias the same object while one is mutable"
250                );
251                checks.push({
    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, "assert");
    ::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_bang(&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, "ptr");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "eq");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&left, &mut _s);
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::ToTokens::to_tokens(&right, &mut _s);
                    _s
                });
            ::quote::__private::push_comma(&mut _s);
            ::quote::ToTokens::to_tokens(&message, &mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    _s
}quote! {
252                    ::core::assert!(!::core::ptr::eq(#left, #right), #message);
253                });
254            }
255        }
256    }
257    if checks.is_empty() {
258        TokenStream::new()
259    } else {
260        // Distinct zero-sized allocations can have identical data addresses.
261        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "if");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "core");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "mem");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "size_of");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&ty, &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_ne(&mut _s);
    ::quote::__private::parse(&mut _s, "0");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            {
                use ::quote::__private::ext::*;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut checks, i) = checks.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let checks =
                        match checks.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    ::quote::ToTokens::to_tokens(&checks, &mut _s);
                }
            }
            _s
        });
    _s
}quote! { if ::core::mem::size_of::<#ty>() != 0 { #(#checks)* } }
262    }
263}
264
265#[cfg(test)]
266mod tests {
267    use super::*;
268
269    #[test]
270    fn native_preflight_preserves_argument_positions_around_self_references() {
271        let types: [syn::Type; 3] = [
272            syn::parse_quote!(&Self),
273            syn::parse_quote!((&mut i32, &i32)),
274            syn::parse_quote!(&mut Self),
275        ];
276        let argv = format_ident!("argv");
277        let output = native_borrow_checks(
278            types
279                .iter()
280                .enumerate()
281                .map(|(i, ty)| (i, format!("arg{i}"), ty)),
282            &argv,
283            None,
284        );
285        let expected =
286            quote! { <(&mut i32, &i32) as ::miniextendr_api::TryFromSexp>::NATIVE_BORROW };
287        let output = output.to_string();
288        assert!(output.contains(&expected.to_string()));
289        assert!(output.contains(&quote! { (*argv.add(1usize), "arg1") }.to_string()));
290        assert!(!output.contains("arg0"));
291        assert!(!output.contains("arg2"));
292        assert!(native_borrow_checks([(0, "other".into(), &types[0])], &argv, None).is_empty());
293    }
294
295    #[test]
296    fn shared_pairs_need_no_guard_but_every_mutable_pair_does() {
297        let handles = vec![
298            (format_ident!("left"), false, "left".into()),
299            (format_ident!("r#type"), false, "type".into()),
300        ];
301        assert!(
302            alias_checks(
303                &quote!(Counter),
304                &handles,
305                Some((quote!(data.cast_const()), false))
306            )
307            .is_empty()
308        );
309        let output = alias_checks(
310            &quote!(Counter),
311            &handles,
312            Some((quote!(data.cast_const()), true)),
313        )
314        .to_string();
315        assert_eq!(output.matches(":: core :: assert !").count(), 2);
316        assert!(output.contains("'self' and 'left'"));
317        assert!(output.contains("'self' and 'type'"));
318        let mut handles = handles;
319        handles[1].1 = true;
320        let output = alias_checks(
321            &quote!(Counter),
322            &handles,
323            Some((quote!(data.cast_const()), true)),
324        )
325        .to_string();
326        assert_eq!(output.matches(":: core :: assert !").count(), 3);
327        assert!(output.contains("'left' and 'type'"));
328    }
329
330    #[test]
331    fn self_arguments_hold_their_borrow_for_the_shim() {
332        let output = borrow(&format_ident!("r#type"), true).to_string();
333        assert!(output.contains("__extptr_type . borrow_mut ()"), "{output}");
334        assert!(
335            output.contains("let r#type = & mut * __mx_guard_type"),
336            "{output}"
337        );
338        let output = borrow(&format_ident!("other"), false).to_string();
339        assert!(output.contains("__extptr_other . borrow ()"), "{output}");
340        assert!(
341            output.contains("let other = & * __mx_guard_other"),
342            "{output}"
343        );
344    }
345}