From df2ae13d3714d0a949de21866b970a588b709a6e Mon Sep 17 00:00:00 2001 From: Kotauskas Date: Tue, 13 Apr 2021 15:52:23 +0300 Subject: [PATCH] Fixed impl Debug for function pointers with HRTBs Function pointers don't implement Debug if some of their arguments are references, as seen in https://github.com/rust-lang/rust/issues/70263. --- examples/basic.rs | 11 +++++++---- src/vtable.rs | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/examples/basic.rs b/examples/basic.rs index f29eba8..80c6830 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -2,14 +2,17 @@ use thin_trait_object::*; #[thin_trait_object] trait Foo { - fn fooify(&self); + fn fooify(&self, extra_data: &str); } impl Foo for String { - fn fooify(&self) { - println!("Fooified a string: {}", self); + fn fooify(&self, extra_data: &str) { + println!( + "Fooified a string: \"{}\" with extra data: \"{}\"", + self, extra_data + ); } } fn main() { - BoxedFoo::new("Hello World!".to_string()).fooify(); + BoxedFoo::new("Hello World!".to_string()).fooify("Another string!"); } diff --git a/src/vtable.rs b/src/vtable.rs index 9d18419..900fe1e 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -17,6 +17,7 @@ use syn::{ FnArg, GenericParam, Generics, + LitStr, Pat, PatIdent, PatType, @@ -78,7 +79,31 @@ pub fn generate_vtable( quote! { #name : #ty } } } - let items = items.iter().cloned().map(VtableItemToFnPtr); + struct VtableItemToDebugImplLine(VtableItem); + impl<'a> ToTokens for VtableItemToDebugImplLine { + fn to_tokens(&self, out: &mut TokenStream) { + out.extend(self.to_token_stream()); + } + fn to_token_stream(&self) -> TokenStream { + let name = self.0.name.clone(); + let namelit = LitStr::new(&name.to_string(), Span::call_site()); + quote! { .field(#namelit, &(self.#name as *mut ())) } + } + } + struct VtableItemToHashImplLine(VtableItem); + impl<'a> ToTokens for VtableItemToHashImplLine { + fn to_tokens(&self, out: &mut TokenStream) { + out.extend(self.to_token_stream()); + } + fn to_token_stream(&self) -> TokenStream { + let name = self.0.name.clone(); + quote! { (self.#name as *mut ()).hash(state) } + } + } + let vtable_entries = items.iter().cloned().map(VtableItemToFnPtr); + let debug_impl_lines = items.iter().cloned().map(VtableItemToDebugImplLine); + let hash_impl_lines = items.iter().cloned().map(VtableItemToHashImplLine); + let name_strlit = LitStr::new(&name.to_string(), Span::call_site()); let size_and_align = if store_layout { quote! { pub size: usize, @@ -88,13 +113,25 @@ pub fn generate_vtable( quote! {} }; quote! { - #[derive(Copy, Clone, Debug, Hash)] + #[derive(Copy, Clone)] #all_attributes #visibility struct #name { #size_and_align - #(pub #items,)* + #(pub #vtable_entries,)* pub drop: unsafe #drop_abi fn(*mut ::core::ffi::c_void), } + impl ::core::fmt::Debug for #name { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_struct(#name_strlit) + #(#debug_impl_lines)* + .finish() + } + } + impl ::core::hash::Hash for #name { + fn hash(&self, state: &mut H) { + #(#hash_impl_lines;)* + } + } } }