From 62010be72ea336dcebfb2bd12c92f43b00df6972 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 15 May 2020 10:24:22 +0200 Subject: [PATCH] vtable: support associated consts --- helper_crates/vtable/macro/macro.rs | 88 ++++++++++++++++------- helper_crates/vtable/src/lib.rs | 18 +++++ helper_crates/vtable/tests/test_vtable.rs | 7 ++ 3 files changed, 86 insertions(+), 27 deletions(-) diff --git a/helper_crates/vtable/macro/macro.rs b/helper_crates/vtable/macro/macro.rs index 26b7ae615..ccabad803 100644 --- a/helper_crates/vtable/macro/macro.rs +++ b/helper_crates/vtable/macro/macro.rs @@ -81,15 +81,15 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream { brace_token: Default::default(), items: Default::default(), }; + let mut generated_trait_assoc_const = None; let mut generated_to_fn_trait = vec![]; let mut generated_type_assoc_fn = vec![]; let mut vtable_ctor = vec![]; for field in &mut fields.named { + let ident = field.ident.as_ref().unwrap(); if let Type::BareFn(f) = &mut field.ty { - let ident = field.ident.as_ref().unwrap(); - let mut sig = Signature { constness: None, asyncness: None, @@ -264,18 +264,15 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream { vis: Visibility::Inherited, defaultness: None, sig: sig.clone(), - block: parse( - if has_self { - quote!({ - // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable - unsafe { (self.vtable.as_ref().#ident)(#call_code) } - }) - } else { - // This should never happen: nobody should be able to access the Trait Object directly. - quote!({ panic!("Calling Sized method on a Trait Object") }) - } - .into(), - ) + block: parse2(if has_self { + quote!({ + // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable + unsafe { (self.vtable.as_ref().#ident)(#call_code) } + }) + } else { + // This should never happen: nobody should be able to access the Trait Object directly. + quote!({ panic!("Calling Sized method on a Trait Object") }) + }) .unwrap(), }); @@ -295,13 +292,10 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream { vis: generated_trait.vis.clone(), defaultness: None, sig, - block: parse( - quote!({ - // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable - unsafe { (self.vtable.as_ref().#ident)(#call_code) } - }) - .into(), - ) + block: parse2(quote!({ + // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable + unsafe { (self.vtable.as_ref().#ident)(#call_code) } + })) .unwrap(), }); @@ -327,17 +321,47 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream { },)); } } else { - return Error::new(field.span(), "member must only be functions") - .to_compile_error() - .into(); + // associated constant + let ty = &field.ty; + + let generated_trait_assoc_const = + generated_trait_assoc_const.get_or_insert_with(|| ItemTrait { + attrs: vec![], + ident: quote::format_ident!("{}Consts", trait_name), + items: vec![], + ..generated_trait.clone() + }); + generated_trait_assoc_const.items.push(TraitItem::Const(TraitItemConst { + attrs: field.attrs.clone(), + const_token: Default::default(), + ident: ident.clone(), + colon_token: Default::default(), + ty: ty.clone(), + default: None, + semi_token: Default::default(), + })); + + vtable_ctor.push(quote!(#ident: T::#ident,)); + + generated_type_assoc_fn.push( + parse2(quote! { + pub fn #ident(&self) -> #ty { + // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable + unsafe { self.vtable.as_ref().#ident } + } + }) + .unwrap(), + ); }; } let vis = input.vis; input.vis = Visibility::Public(VisPublic { pub_token: Default::default() }); - /*let (fields_name, fields_type): (Vec<_>, Vec<_>) = - fields.named.iter().map(|f| (f.ident.clone().unwrap(), f.ty.clone())).unzip();*/ + let new_trait_extra = generated_trait_assoc_const.as_ref().map(|x| { + let i = &x.ident; + quote!(+ #i) + }); let result = quote!( #[allow(non_snake_case)] @@ -350,7 +374,7 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream { impl #vtable_name { // unfortunately cannot be const in stable rust because of the bounds (depends on rfc 2632) - pub /*const*/ fn new() -> Self { + pub /*const*/ fn new() -> Self { Self { #(#vtable_ctor)* } @@ -358,6 +382,8 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream { } #generated_trait + #generated_trait_assoc_const + struct #impl_name { _private: [u8; 0] } /// This structure is highly unsafe, as it just has pointers. One could call trait functions @@ -388,12 +414,20 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream { type Trait = dyn #trait_name; type VTable = #vtable_name; type TraitObject = #to_name; + type Type = #type_name; + #[inline] unsafe fn map_to(from: &Self::TraitObject) -> &Self::Trait { from } + #[inline] unsafe fn map_to_mut(from: &mut Self::TraitObject) -> &mut Self::Trait { from } + #[inline] unsafe fn get_ptr(from: &Self::TraitObject) -> core::ptr::NonNull { from.ptr.cast() } + #[inline] unsafe fn get_vtable(from: &Self::TraitObject) -> core::ptr::NonNull { from.vtable } + #[inline] unsafe fn from_raw(vtable: core::ptr::NonNull, ptr: core::ptr::NonNull) -> Self::TraitObject { #to_name { vtable, ptr : ptr.cast() } } + #[inline] + unsafe fn get_type(from: &Self::TraitObject) -> Self::Type { #type_name::from_raw(from.vtable) } } #drop_impl diff --git a/helper_crates/vtable/src/lib.rs b/helper_crates/vtable/src/lib.rs index 75dd3925a..ccc0595ef 100644 --- a/helper_crates/vtable/src/lib.rs +++ b/helper_crates/vtable/src/lib.rs @@ -9,10 +9,14 @@ pub unsafe trait VTableMeta { /// that's the vtable struct `HelloVTable` type VTable; + /// That's the safe wrapper around a vtable pointer (`HelloType`) + type Type; + /// That's the trait object that implements the trait. /// NOTE: the size must be 2*size_of type TraitObject: Copy; + /// That maps from the tait object from the trait iteself /// (In other word, return 'to' since 'to' implements trait, /// but we can't represent that in rust right now, hence these helper) @@ -29,6 +33,11 @@ pub unsafe trait VTableMeta { /// Create a trait object from its raw parts unsafe fn from_raw(vtable: NonNull, ptr: NonNull) -> Self::TraitObject; + + /// return a safe pointer around the vtable + unsafe fn get_type(from: &Self::TraitObject) -> Self::Type; + + } pub trait VTableMetaDrop: VTableMeta { @@ -84,6 +93,9 @@ impl VBox { pub unsafe fn from_raw(vtable: NonNull, ptr: NonNull) -> Self { Self {inner : T::from_raw(vtable, ptr)} } + pub fn get_type(&self) -> T::Type { + unsafe { T::get_type(&self.inner) } + } } /* @@ -144,6 +156,9 @@ impl<'a, T: ?Sized + VTableMeta> VRef<'a, T> { pub unsafe fn from_raw(vtable: NonNull, ptr: NonNull) -> Self { Self {inner : T::from_raw(vtable, ptr), _phantom: PhantomData } } + pub fn get_type(&self) -> T::Type { + unsafe { T::get_type(&self.inner) } + } } pub struct VRefMut<'a, T: ?Sized + VTableMeta> { @@ -191,4 +206,7 @@ impl<'a, T: ?Sized + VTableMeta> VRefMut<'a, T> { pub fn into_ref(self) -> VRef<'a, T> { unsafe { VRef::from_inner(VRefMut::inner(&self)) } } + pub fn get_type(&self) -> T::Type { + unsafe { T::get_type(&self.inner) } + } } diff --git a/helper_crates/vtable/tests/test_vtable.rs b/helper_crates/vtable/tests/test_vtable.rs index 0408fab7e..147119675 100644 --- a/helper_crates/vtable/tests/test_vtable.rs +++ b/helper_crates/vtable/tests/test_vtable.rs @@ -8,6 +8,8 @@ struct HelloVTable { assoc: fn(*const HelloVTable) -> isize, drop: fn(VRefMut<'_, HelloVTable>), + + CONSTANT: usize, } #[derive(Debug)] @@ -34,6 +36,9 @@ impl Hello for SomeStruct { 32 } } +impl HelloConsts for SomeStruct { + const CONSTANT: usize = 88; +} #[test] fn test() { @@ -41,10 +46,12 @@ fn test() { let mut vt = HelloVTable::new::(); let vt = unsafe { HelloType::from_raw(std::ptr::NonNull::from(&mut vt)) }; assert_eq!(vt.assoc(), 32); + assert_eq!(vt.CONSTANT(), 88); let mut bx = vt.construct(89); assert_eq!(bx.foo(1), 90); assert_eq!(bx.foo_mut(6), 95); assert_eq!(bx.foo(2), 97); + assert_eq!(bx.get_type().CONSTANT(), 88); }