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(),
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 {
if let Type::BareFn(f) = &mut field.ty {
let ident = field.ident.as_ref().unwrap();
if let Type::BareFn(f) = &mut field.ty {
let mut sig = Signature {
constness: None,
asyncness: None,
@ -264,8 +264,7 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
vis: Visibility::Inherited,
defaultness: None,
sig: sig.clone(),
block: parse(
if has_self {
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) }
@ -273,9 +272,7 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
} 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(),
)
})
.unwrap(),
});
@ -295,13 +292,10 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
vis: generated_trait.vis.clone(),
defaultness: None,
sig,
block: parse(
quote!({
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) }
})
.into(),
)
}))
.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<T: #trait_name>() -> Self {
pub /*const*/ fn new<T: #trait_name #new_trait_extra>() -> 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<u8> { from.ptr.cast() }
#[inline]
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
{ #to_name { vtable, ptr : ptr.cast() } }
#[inline]
unsafe fn get_type(from: &Self::TraitObject) -> Self::Type { #type_name::from_raw(from.vtable) }
}
#drop_impl

View file

@ -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<usize>
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<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 {
@ -84,6 +93,9 @@ impl<T: ?Sized + VTableMetaDrop> VBox<T> {
pub unsafe fn from_raw(vtable: NonNull<T::VTable>, ptr: NonNull<u8>) -> 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<T::VTable>, ptr: NonNull<u8>) -> 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) }
}
}

View file

@ -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::<SomeStruct>();
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);
}