abi_stable_derive/sabi_trait/
methods_tokenizer.rs

1//! Contains the MethodsTokenizer type,
2//! which is used to print the definition of the method in different places.
3//!
4//! Where this is used is determined by WhichItem:
5//!
6//! - `WhichItem::Trait`:
7//!     outputs the method in the trait definition.
8//!
9//! - `WhichItem::TraitImpl`:
10//!     outputs the method in the trait implemetation for the generated trait object.
11//!
12//! - `WhichItem::TraitObjectImpl`:
13//!     outputs the methods in the inherent implemetation of the generated trait object.
14//!
15//! - `WhichItem::VtableDecl`:
16//!     outputs the fields of the trait object vtable.
17//!
18//! - `WhichItem::VtableImpl`:
19//!     outputs the methods used to construct the vtable.
20//!
21//!
22
23use super::{lifetime_unelider::BorrowKind, *};
24
25use as_derive_utils::to_token_fn::ToTokenFnMut;
26
27use quote::TokenStreamExt;
28
29#[derive(Debug, Copy, Clone)]
30pub struct MethodsTokenizer<'a> {
31    pub(crate) trait_def: &'a TraitDefinition<'a>,
32    pub(crate) which_item: WhichItem,
33}
34
35#[derive(Debug, Copy, Clone)]
36pub struct MethodTokenizer<'a> {
37    trait_def: &'a TraitDefinition<'a>,
38    method: &'a TraitMethod<'a>,
39    which_item: WhichItem,
40}
41
42impl<'a> ToTokens for MethodsTokenizer<'a> {
43    fn to_tokens(&self, ts: &mut TokenStream2) {
44        for method in &self.trait_def.methods {
45            MethodTokenizer {
46                trait_def: self.trait_def,
47                method,
48                which_item: self.which_item,
49            }
50            .to_tokens(ts);
51        }
52    }
53}
54
55impl<'a> ToTokens for MethodTokenizer<'a> {
56    fn to_tokens(&self, ts: &mut TokenStream2) {
57        let which_item = self.which_item;
58        let method = self.method;
59        let trait_def = self.trait_def;
60        let ctokens = trait_def.ctokens;
61        // is_method: Whether this is a method,instead of an associated function or field.
62        //
63        // vis: the visibility of the generated method,
64        //      None if it's implicit,Some(_) if it's explicit.
65        let (is_method, vis) = match which_item {
66            WhichItem::Trait | WhichItem::TraitImpl => (true, None),
67            WhichItem::TraitObjectImpl => (true, Some(trait_def.submod_vis)),
68            WhichItem::VtableDecl | WhichItem::VtableImpl => (false, Some(trait_def.submod_vis)),
69        };
70
71        // The default implementation block used both by:
72        // - the trait definition.
73        // - the trait object inherent impl
74        //      (for the case where the method doesn't exist in the vtable).
75        let default_ = method
76            .default
77            .as_ref()
78            .filter(|_| !method.disable_inherent_default);
79
80        let lifetimes = Some(&method.lifetimes)
81            .filter(|l| !l.is_empty())
82            .into_iter();
83
84        let method_name = method.name;
85        let method_span = method_name.span();
86
87        let self_ty = if is_method {
88            quote_spanned!(method_span=> Self)
89        } else {
90            quote_spanned!(method_span=> _Self)
91        };
92
93        struct WriteLifetime<'a>(Option<&'a syn::Lifetime>);
94
95        impl ToTokens for WriteLifetime<'_> {
96            fn to_tokens(&self, ts: &mut TokenStream2) {
97                if let Some(lt) = self.0 {
98                    lt.to_tokens(ts)
99                } else {
100                    ts.append_all(quote!('_))
101                }
102            }
103        }
104
105        let self_param = match (is_method, &method.self_param) {
106            (
107                true,
108                SelfParam::ByRef {
109                    lifetime,
110                    is_mutable: false,
111                },
112            ) => {
113                quote_spanned!(method_span=> & #lifetime self)
114            }
115            (
116                true,
117                SelfParam::ByRef {
118                    lifetime,
119                    is_mutable: true,
120                },
121            ) => {
122                quote_spanned!(method_span=> & #lifetime mut self)
123            }
124            (true, SelfParam::ByVal) => {
125                quote_spanned!(method_span=> self)
126            }
127            (
128                false,
129                SelfParam::ByRef {
130                    lifetime,
131                    is_mutable: false,
132                },
133            ) => {
134                let lifetime = WriteLifetime(*lifetime);
135                quote_spanned!(method_span=> _self: __sabi_re::RRef<#lifetime, ()>)
136            }
137            (
138                false,
139                SelfParam::ByRef {
140                    lifetime,
141                    is_mutable: true,
142                },
143            ) => {
144                let lifetime = WriteLifetime(*lifetime);
145                quote_spanned!(method_span=> _self: __sabi_re::RMut<#lifetime, ()>)
146            }
147            (false, SelfParam::ByVal) => {
148                quote_spanned!(method_span=> _self:*mut ())
149            }
150        };
151
152        let param_names_a = method.params.iter().map(move |param| {
153            ToTokenFnMut::new(move |ts| match which_item {
154                WhichItem::Trait => {
155                    param.pattern.to_tokens(ts);
156                }
157                _ => {
158                    param.name.to_tokens(ts);
159                }
160            })
161        });
162        let param_ty = method.params.iter().map(|param| &param.ty);
163        let param_names_c = param_names_a.clone();
164        let param_names_d = param_names_a.clone();
165        let param_names_e = method.params.iter().map(|x| x.pattern);
166        let return_ty = method.output.iter();
167
168        let self_is_sized_bound = Some(&ctokens.self_sized)
169            .filter(|_| is_method && method.self_param == SelfParam::ByVal);
170
171        let abi = match which_item {
172            WhichItem::VtableImpl => Some(&ctokens.extern_c),
173            _ => method.abi,
174        };
175
176        let user_where_clause = method.where_clause.get_tokenizer(ctokens);
177
178        let other_attrs = if which_item == WhichItem::Trait {
179            method.other_attrs
180        } else {
181            &[]
182        };
183
184        if WhichItem::VtableImpl == which_item {
185            ts.append_all(quote_spanned!(method_span=> #[doc(hidden)] ));
186        }
187
188        if WhichItem::VtableDecl == which_item {
189            let optional_field = default_.as_ref().map(|_| &ctokens.missing_field_option);
190            let derive_attrs = method.derive_attrs;
191
192            quote_spanned!( method_span=>
193                #optional_field
194                #(#derive_attrs)*
195                #vis #method_name:
196                    #(for< #(#lifetimes,)* >)*
197                    unsafe extern "C" fn(
198                        #self_param,
199                        #( #param_names_a:#param_ty ,)*
200                    ) #(-> #return_ty )*
201            )
202        } else {
203            let inherent_method_docs = ToTokenFnMut::new(|ts| {
204                if WhichItem::TraitObjectImpl != which_item {
205                    return;
206                }
207                let trait_name = trait_def.name;
208                let m_docs = format!(
209                    "This is the inherent equivalent of \
210                     [the trait method of the same name](./trait.{TN}.html#tymethod.{TM})\
211                    ",
212                    TN = trait_name,
213                    TM = method_name,
214                );
215
216                ts.append_all(quote!(#[doc = #m_docs]));
217            });
218
219            let unsafety = match which_item {
220                WhichItem::VtableImpl => Some(&ctokens.unsafe_),
221                _ => method.unsafety,
222            };
223
224            quote_spanned!(method_span=>
225                #[allow(clippy::let_and_return)]
226                #(#other_attrs)*
227                #inherent_method_docs
228                #vis #unsafety #abi fn #method_name #(< #(#lifetimes,)* >)* (
229                    #self_param,
230                    #( #param_names_a:#param_ty ,)*
231                ) #(-> #return_ty )*
232                where
233                    #self_is_sized_bound
234                    #user_where_clause
235            )
236        }
237        .to_tokens(ts);
238
239        let ptr_constraint = match &method.self_param {
240            SelfParam::ByRef {
241                is_mutable: false, ..
242            } => &ctokens.ptr_ref_bound,
243            SelfParam::ByRef {
244                is_mutable: true, ..
245            } => &ctokens.ptr_mut_bound,
246            SelfParam::ByVal => &ctokens.ptr_val_bound,
247        };
248
249        let output_safety = |output: &mut TokenStream2, input: TokenStream2| {
250            output.append_all(if let Some(safety) = method.unsafety {
251                quote_spanned!(safety.span => { #safety{ #input } })
252            } else {
253                quote_spanned!(method_span => { #input })
254            });
255        };
256
257        match (which_item, &method.self_param) {
258            (WhichItem::Trait, _) => {
259                method.default.as_ref().map(|x| x.block).to_tokens(ts);
260                method.semicolon.to_tokens(ts);
261            }
262            (WhichItem::TraitImpl, _) => {
263                output_safety(
264                    ts,
265                    quote_spanned!(method_span =>
266                        self.#method_name(#(#param_names_c,)*)
267                    ),
268                );
269            }
270            (WhichItem::TraitObjectImpl, _) => {
271                let method_call = match &method.self_param {
272                    SelfParam::ByRef {
273                        is_mutable: false, ..
274                    } => {
275                        quote_spanned!(method_span=>
276                            __method(self.obj.sabi_as_rref(),#(#param_names_c,)*)
277                        )
278                    }
279                    SelfParam::ByRef {
280                        is_mutable: true, ..
281                    } => {
282                        quote_spanned!(method_span=>
283                            __method(self.obj.sabi_as_rmut(),#(#param_names_c,)*)
284                        )
285                    }
286                    SelfParam::ByVal => {
287                        quote_spanned!(method_span=>
288                            self.obj.sabi_with_value(
289                                move|_self|__method(
290                                    __sabi_re::MovePtr::into_raw(_self) as *mut (),
291                                    #(#param_names_c,)*
292                                )
293                            )
294                        )
295                    }
296                };
297
298                match default_ {
299                    Some(default_) => {
300                        let block = &default_.block;
301                        ts.append_all(quote_spanned!(method_span=>
302                                #ptr_constraint
303                            {
304                                match self.sabi_vtable().#method_name() {
305                                    Some(__method)=>{
306                                        unsafe{
307                                            #method_call
308                                        }
309                                    }
310                                    None=>{
311                                        #(
312                                            let #param_names_e=#param_names_d;
313                                        )*
314                                        #block
315                                    }
316                                }
317                            }
318                        ));
319                    }
320                    None => {
321                        ts.append_all(quote_spanned!(method_span=>
322                                #ptr_constraint
323                            {
324                                let __method=self.sabi_vtable().#method_name();
325                                unsafe{
326                                    #method_call
327                                }
328                            }
329                        ));
330                    }
331                }
332            }
333            (WhichItem::VtableDecl, _) => {
334                quote_spanned!(method_span=> , ).to_tokens(ts);
335            }
336            (WhichItem::VtableImpl, SelfParam::ByRef { is_mutable, .. }) => {
337                let mut_token = ToTokenFnMut::new(|ts| {
338                    if *is_mutable {
339                        syn::token::Mut { span: method_span }.to_tokens(ts);
340                    }
341                });
342
343                let ret = syn::Ident::new("ret", proc_macro2::Span::call_site());
344
345                let transmute_ret = match method.return_borrow_kind {
346                    Some(BorrowKind::Reference) => {
347                        quote_spanned!(method_span=> ::std::mem::transmute(#ret) )
348                    }
349                    Some(BorrowKind::MutReference) => {
350                        quote_spanned!(method_span=> ::std::mem::transmute(#ret) )
351                    }
352                    Some(BorrowKind::Other) => {
353                        // Motivation:
354                        // We need to use this transmute to return a borrow from `_self`,
355                        // without adding a `_Self: '_self` bound,
356                        // which causes compilation errors due to how HRTB are handled.
357                        // The correctness of the lifetime is guaranteed by the trait definition.
358                        quote_spanned!(method_span=> __sabi_re::transmute_ignore_size(#ret) )
359                    }
360                    None => quote_spanned!(method_span=> #ret ),
361                };
362
363                ts.append_all(quote_spanned!(method_span=>{
364                    unsafe{
365                        let #ret = ::abi_stable::extern_fn_panic_handling!{no_early_return;
366                            __Trait::#method_name(
367                                &#mut_token *_self.transmute_into_raw::<#self_ty>(),
368                                #(#param_names_c,)*
369                            )
370                        };
371
372                        #transmute_ret
373                    }
374                }));
375            }
376            (WhichItem::VtableImpl, SelfParam::ByVal) => {
377                ts.append_all(quote_spanned!(method_span=>{
378                    ::abi_stable::extern_fn_panic_handling!{no_early_return; unsafe{
379                        __Trait::#method_name(
380                            (_self as *mut #self_ty).read(),#(#param_names_c,)*
381                        )
382                    }}
383                }));
384            }
385        }
386    }
387}