vtable: support associated consts

This commit is contained in:
Olivier Goffart 2020-05-15 10:24:22 +02:00
parent 544cb1a198
commit 62010be72e
3 changed files with 86 additions and 27 deletions

View file

@ -81,15 +81,15 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
brace_token: Default::default(), brace_token: Default::default(),
items: Default::default(), items: Default::default(),
}; };
let mut generated_trait_assoc_const = None;
let mut generated_to_fn_trait = vec![]; let mut generated_to_fn_trait = vec![];
let mut generated_type_assoc_fn = vec![]; let mut generated_type_assoc_fn = vec![];
let mut vtable_ctor = vec![]; let mut vtable_ctor = vec![];
for field in &mut fields.named { for field in &mut fields.named {
let ident = field.ident.as_ref().unwrap();
if let Type::BareFn(f) = &mut field.ty { if let Type::BareFn(f) = &mut field.ty {
let ident = field.ident.as_ref().unwrap();
let mut sig = Signature { let mut sig = Signature {
constness: None, constness: None,
asyncness: None, asyncness: None,
@ -264,18 +264,15 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
vis: Visibility::Inherited, vis: Visibility::Inherited,
defaultness: None, defaultness: None,
sig: sig.clone(), sig: sig.clone(),
block: parse( block: parse2(if has_self {
if has_self { quote!({
quote!({ // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
// 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) }
unsafe { (self.vtable.as_ref().#ident)(#call_code) } })
}) } else {
} else { // This should never happen: nobody should be able to access the Trait Object directly.
// This should never happen: nobody should be able to access the Trait Object directly. quote!({ panic!("Calling Sized method on a Trait Object") })
quote!({ panic!("Calling Sized method on a Trait Object") }) })
}
.into(),
)
.unwrap(), .unwrap(),
}); });
@ -295,13 +292,10 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
vis: generated_trait.vis.clone(), vis: generated_trait.vis.clone(),
defaultness: None, defaultness: None,
sig, sig,
block: parse( block: parse2(quote!({
quote!({ // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
// 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) }
unsafe { (self.vtable.as_ref().#ident)(#call_code) } }))
})
.into(),
)
.unwrap(), .unwrap(),
}); });
@ -327,17 +321,47 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
},)); },));
} }
} else { } else {
return Error::new(field.span(), "member must only be functions") // associated constant
.to_compile_error() let ty = &field.ty;
.into();
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; let vis = input.vis;
input.vis = Visibility::Public(VisPublic { pub_token: Default::default() }); input.vis = Visibility::Public(VisPublic { pub_token: Default::default() });
/*let (fields_name, fields_type): (Vec<_>, Vec<_>) = let new_trait_extra = generated_trait_assoc_const.as_ref().map(|x| {
fields.named.iter().map(|f| (f.ident.clone().unwrap(), f.ty.clone())).unzip();*/ let i = &x.ident;
quote!(+ #i)
});
let result = quote!( let result = quote!(
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -350,7 +374,7 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
impl #vtable_name { impl #vtable_name {
// unfortunately cannot be const in stable rust because of the bounds (depends on rfc 2632) // unfortunately cannot be const in stable rust because of the bounds (depends on rfc 2632)
pub /*const*/ fn new<T: #trait_name>() -> Self { pub /*const*/ fn new<T: #trait_name #new_trait_extra>() -> Self {
Self { Self {
#(#vtable_ctor)* #(#vtable_ctor)*
} }
@ -358,6 +382,8 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
} }
#generated_trait #generated_trait
#generated_trait_assoc_const
struct #impl_name { _private: [u8; 0] } struct #impl_name { _private: [u8; 0] }
/// This structure is highly unsafe, as it just has pointers. One could call trait functions /// 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 Trait = dyn #trait_name;
type VTable = #vtable_name; type VTable = #vtable_name;
type TraitObject = #to_name; type TraitObject = #to_name;
type Type = #type_name;
#[inline]
unsafe fn map_to(from: &Self::TraitObject) -> &Self::Trait { from } unsafe fn map_to(from: &Self::TraitObject) -> &Self::Trait { from }
#[inline]
unsafe fn map_to_mut(from: &mut Self::TraitObject) -> &mut Self::Trait { from } unsafe fn map_to_mut(from: &mut Self::TraitObject) -> &mut Self::Trait { from }
#[inline]
unsafe fn get_ptr(from: &Self::TraitObject) -> core::ptr::NonNull<u8> { from.ptr.cast() } unsafe fn get_ptr(from: &Self::TraitObject) -> core::ptr::NonNull<u8> { from.ptr.cast() }
#[inline]
unsafe fn get_vtable(from: &Self::TraitObject) -> core::ptr::NonNull<Self::VTable> { from.vtable } unsafe fn get_vtable(from: &Self::TraitObject) -> core::ptr::NonNull<Self::VTable> { from.vtable }
#[inline]
unsafe fn from_raw(vtable: core::ptr::NonNull<Self::VTable>, ptr: core::ptr::NonNull<u8>) -> Self::TraitObject unsafe fn from_raw(vtable: core::ptr::NonNull<Self::VTable>, ptr: core::ptr::NonNull<u8>) -> Self::TraitObject
{ #to_name { vtable, ptr : ptr.cast() } } { #to_name { vtable, ptr : ptr.cast() } }
#[inline]
unsafe fn get_type(from: &Self::TraitObject) -> Self::Type { #type_name::from_raw(from.vtable) }
} }
#drop_impl #drop_impl

View file

@ -9,10 +9,14 @@ pub unsafe trait VTableMeta {
/// that's the vtable struct `HelloVTable` /// that's the vtable struct `HelloVTable`
type VTable; type VTable;
/// That's the safe wrapper around a vtable pointer (`HelloType`)
type Type;
/// That's the trait object that implements the trait. /// That's the trait object that implements the trait.
/// NOTE: the size must be 2*size_of<usize> /// NOTE: the size must be 2*size_of<usize>
type TraitObject: Copy; type TraitObject: Copy;
/// That maps from the tait object from the trait iteself /// That maps from the tait object from the trait iteself
/// (In other word, return 'to' since 'to' implements trait, /// (In other word, return 'to' since 'to' implements trait,
/// but we can't represent that in rust right now, hence these helper) /// 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 /// Create a trait object from its raw parts
unsafe fn from_raw(vtable: NonNull<Self::VTable>, ptr: NonNull<u8>) -> Self::TraitObject; unsafe fn from_raw(vtable: NonNull<Self::VTable>, ptr: NonNull<u8>) -> Self::TraitObject;
/// return a safe pointer around the vtable
unsafe fn get_type(from: &Self::TraitObject) -> Self::Type;
} }
pub trait VTableMetaDrop: VTableMeta { pub trait VTableMetaDrop: VTableMeta {
@ -84,6 +93,9 @@ impl<T: ?Sized + VTableMetaDrop> VBox<T> {
pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self { pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self {
Self {inner : T::from_raw(vtable, ptr)} 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<T::VTable>, ptr: NonNull<u8>) -> Self { pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> Self {
Self {inner : T::from_raw(vtable, ptr), _phantom: PhantomData } 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> { 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> { pub fn into_ref(self) -> VRef<'a, T> {
unsafe { VRef::from_inner(VRefMut::inner(&self)) } unsafe { VRef::from_inner(VRefMut::inner(&self)) }
} }
pub fn get_type(&self) -> T::Type {
unsafe { T::get_type(&self.inner) }
}
} }

View file

@ -8,6 +8,8 @@ struct HelloVTable {
assoc: fn(*const HelloVTable) -> isize, assoc: fn(*const HelloVTable) -> isize,
drop: fn(VRefMut<'_, HelloVTable>), drop: fn(VRefMut<'_, HelloVTable>),
CONSTANT: usize,
} }
#[derive(Debug)] #[derive(Debug)]
@ -34,6 +36,9 @@ impl Hello for SomeStruct {
32 32
} }
} }
impl HelloConsts for SomeStruct {
const CONSTANT: usize = 88;
}
#[test] #[test]
fn test() { fn test() {
@ -41,10 +46,12 @@ fn test() {
let mut vt = HelloVTable::new::<SomeStruct>(); let mut vt = HelloVTable::new::<SomeStruct>();
let vt = unsafe { HelloType::from_raw(std::ptr::NonNull::from(&mut vt)) }; let vt = unsafe { HelloType::from_raw(std::ptr::NonNull::from(&mut vt)) };
assert_eq!(vt.assoc(), 32); assert_eq!(vt.assoc(), 32);
assert_eq!(vt.CONSTANT(), 88);
let mut bx = vt.construct(89); let mut bx = vt.construct(89);
assert_eq!(bx.foo(1), 90); assert_eq!(bx.foo(1), 90);
assert_eq!(bx.foo_mut(6), 95); assert_eq!(bx.foo_mut(6), 95);
assert_eq!(bx.foo(2), 97); assert_eq!(bx.foo(2), 97);
assert_eq!(bx.get_type().CONSTANT(), 88);
} }