Skip to content

Commit

Permalink
compensate for regression in 1.78 (rust-lang/rust#123281)
Browse files Browse the repository at this point in the history
  • Loading branch information
p-avital committed May 3, 2024
1 parent aa7afbe commit a752895
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 22 deletions.
7 changes: 7 additions & 0 deletions stabby-abi/src/alloc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,13 @@ impl<T, Alloc> AllocPtr<T, Alloc> {
marker: PhantomData,
}
}
/// Casts an allocated pointer.
pub const fn cast<U>(self) -> AllocPtr<U, Alloc> {
AllocPtr {
ptr: self.ptr.cast(),
marker: PhantomData,
}
}
/// The offset between `self.ptr` and the prefix.
pub const fn prefix_skip() -> usize {
AllocPrefix::<Alloc>::skip_to::<T>()
Expand Down
11 changes: 5 additions & 6 deletions stabby-abi/src/fatptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ impl<'a, Vt: Copy + 'a> DynRef<'a, Vt> {
where
Vt: PartialEq + IConstConstructor<'a, T>,
{
(self.vtable == Vt::VTABLE).then(|| unsafe { self.ptr.as_ref() })
(self.vtable == Vt::vtable()).then(|| unsafe { self.ptr.as_ref() })
}
/// Downcasts the reference based on its reflection report.
pub fn stable_downcast<T: crate::IStable, Path>(&self) -> Option<&T>
Expand Down Expand Up @@ -286,8 +286,7 @@ impl<'a, P: IPtrOwned + IPtr, Vt: HasDropVt + 'a> Dyn<'a, P, Vt> {
where
Vt: PartialEq + Copy + IConstConstructor<'a, T>,
{
eprintln!("{:p} vs {:p}", self.vtable(), Vt::VTABLE);
(self.vtable == Vt::VTABLE).then(|| unsafe { self.ptr.as_ref() })
(self.vtable == Vt::vtable()).then(|| unsafe { self.ptr.as_ref() })
}
/// Downcasts the reference based on its reflection report.
pub fn stable_downcast_ref<T: crate::IStable, Path>(&self) -> Option<&T>
Expand Down Expand Up @@ -321,7 +320,7 @@ impl<'a, P: IPtrOwned + IPtr, Vt: HasDropVt + 'a> Dyn<'a, P, Vt> {
Vt: PartialEq + Copy + IConstConstructor<'a, T>,
P: IPtrMut,
{
(self.vtable == Vt::VTABLE).then(|| unsafe { self.ptr.as_mut() })
(self.vtable == Vt::vtable()).then(|| unsafe { self.ptr.as_mut() })
}
/// Downcasts the mutable reference based on its reflection report.
pub fn stable_downcast_mut<T: crate::IStable, Path>(&mut self) -> Option<&mut T>
Expand All @@ -344,7 +343,7 @@ where
fn from(value: P) -> Self {
Self {
ptr: core::mem::ManuallyDrop::new(value.anonimize()),
vtable: Vt::VTABLE,
vtable: Vt::vtable(),
unsend: core::marker::PhantomData,
}
}
Expand All @@ -363,7 +362,7 @@ impl<'a, T, Vt: Copy + IConstConstructor<'a, T>> From<&'a T> for DynRef<'a, Vt>
unsafe {
DynRef {
ptr: core::mem::transmute(value),
vtable: Vt::VTABLE,
vtable: Vt::vtable(),
unsend: core::marker::PhantomData,
}
}
Expand Down
159 changes: 144 additions & 15 deletions stabby-abi/src/vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,140 @@
// Pierre Avital, <[email protected]>
//

use crate as stabby;
use crate::{self as stabby};
use core::{
hash::{Hash, Hasher},
marker::PhantomData,
ptr::NonNull,
};

#[rustversion::nightly]
/// Implementation detail for stabby's version of dyn traits.
/// Any type that implements a trait `ITrait` must implement `IConstConstructor<VtITrait>` for `stabby::dyn!(Ptr<ITrait>)::from(value)` to work.
pub trait IConstConstructor<'a, Source>: 'a + Copy + core::marker::Freeze {
/// The vtable.
const VTABLE: &'a Self;
const VTABLE: Self;
/// A reference to the vtable
const VTABLE_REF: &'a Self = &Self::VTABLE;
/// Returns the reference to the vtable
fn vtable() -> &'a Self {
Self::VTABLE_REF
}
}
#[rustversion::not(nightly)]

#[rustversion::before(1.78.0)]
/// Implementation detail for stabby's version of dyn traits.
/// Any type that implements a trait `ITrait` must implement `IConstConstructor<VtITrait>` for `stabby::dyn!(Ptr<ITrait>)::from(value)` to work.
pub trait IConstConstructor<'a, Source>: 'a + Copy {
/// The vtable.
const VTABLE: &'a Self;
const VTABLE: Self;
/// A reference to the vtable
const VTABLE_REF: &'a Self = &Self::VTABLE;
/// Returns the reference to the vtable
fn vtable() -> &'a Self {
Self::VTABLE_REF
}
}

#[cfg(feature = "libc")]
#[rustversion::all(since(1.78.0), not(nightly))]
static VTABLES: core::sync::atomic::AtomicPtr<
crate::alloc::vec::Vec<(
u64,
crate::alloc::AllocPtr<*const (), crate::alloc::DefaultAllocator>,
)>,
> = core::sync::atomic::AtomicPtr::new(core::ptr::null_mut());
#[cfg(feature = "libc")]
#[rustversion::all(since(1.78.0), not(nightly))]
/// Implementation detail for stabby's version of dyn traits.
/// Any type that implements a trait `ITrait` must implement `IConstConstructor<VtITrait>` for `stabby::dyn!(Ptr<ITrait>)::from(value)` to work.
pub trait IConstConstructor<'a, Source>: 'a + Copy + core::hash::Hash + core::fmt::Debug {
/// The vtable.
const VTABLE: Self;
/// Returns the reference to the vtable
fn vtable() -> &'a Self {
use crate::alloc::{boxed::Box, vec::Vec, AllocPtr, DefaultAllocator};
let vtable = Self::VTABLE;
#[allow(deprecated)]
let hash = {
let mut hasher = core::hash::SipHasher::new();
vtable.hash(&mut hasher);
hasher.finish()
};
fn insert_vtable(hash: u64, vtable: &[*const ()]) -> AllocPtr<*const (), DefaultAllocator> {
let mut search_start = 0;
let mut allocated_vt = None;
let mut vtables = VTABLES.load(core::sync::atomic::Ordering::SeqCst);
loop {
let vts = match unsafe { vtables.as_ref() } {
None => [].as_slice(),
Some(vts) => match vts[search_start..]
.iter()
.find_map(|e| (e.0 == hash).then_some(e.1))
{
Some(vt) => return vt,
None => vts,
},
};
let vt = allocated_vt.unwrap_or_else(|| {
let mut ptr =
AllocPtr::alloc_array(&mut DefaultAllocator::new(), vtable.len()).unwrap();
unsafe {
core::ptr::copy_nonoverlapping(vtable.as_ptr(), ptr.as_mut(), vtable.len())
};
ptr
});
allocated_vt = Some(vt);
let updated = VTABLES.load(core::sync::atomic::Ordering::SeqCst);
if !core::ptr::eq(vtables, updated) {
vtables = updated;
continue;
}
let mut vec = Vec::with_capacity(vts.len() + 1);
vec.copy_extend(vts);
vec.push((hash, vt));
let mut vec = Box::into_raw(Box::new(vec));
match VTABLES.compare_exchange(
updated,
unsafe { vec.as_mut() },
core::sync::atomic::Ordering::SeqCst,
core::sync::atomic::Ordering::SeqCst,
) {
Ok(updated) => {
if let Some(updated) = NonNull::new(updated) {
unsafe {
Box::from_raw(AllocPtr {
ptr: updated,
marker: PhantomData::<DefaultAllocator>,
})
};
}
return vt;
}
Err(new_vtables) => {
search_start = vts.len();
vtables = new_vtables;
unsafe { Box::from_raw(vec) };
}
}
}
}
unsafe {
let vtable = core::slice::from_raw_parts(
(&vtable as *const Self).cast(),
core::mem::size_of::<Self>() / core::mem::size_of::<*const ()>(),
);
let vt = insert_vtable(hash, vtable).cast().as_ref();
debug_assert_eq!(
core::slice::from_raw_parts(
(vt as *const Self).cast::<*const ()>(),
core::mem::size_of::<Self>() / core::mem::size_of::<*const ()>(),
),
vtable
);
vt
}
}
}

/// Implementation detail for stabby's version of dyn traits.
Expand All @@ -42,7 +161,7 @@ pub struct T<T>(T);
/// You should _always_ use `stabby::vtable!(Trait1 + Trait2 + ...)` to generate this type,
/// as this macro will ensure that traits are ordered consistently in the vtable.
#[stabby::stabby]
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct VTable<Head, Tail = VtDrop> {
/// The rest of the vtable.
///
Expand All @@ -63,13 +182,13 @@ where
Head: IConstConstructor<'a, T>,
Tail: IConstConstructor<'a, T>,
{
const VTABLE: &'a VTable<Head, Tail> = &VTable {
head: *Head::VTABLE,
tail: *Tail::VTABLE,
const VTABLE: VTable<Head, Tail> = VTable {
head: Head::VTABLE,
tail: Tail::VTABLE,
};
}
impl<'a, T> IConstConstructor<'a, T> for () {
const VTABLE: &'a () = &();
const VTABLE: () = ();
}
impl<Head, Tail> TransitiveDeref<Head, H> for VTable<Head, Tail> {
fn tderef(&self) -> &Head {
Expand Down Expand Up @@ -122,11 +241,21 @@ impl<Head, Tail: HasSyncVt> HasSyncVt for VTable<Head, Tail> {}
// DROP
/// The vtable to drop a value in place
#[stabby::stabby]
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Eq)]
pub struct VtDrop {
/// The [`Drop::drop`] function, shimmed with the C calling convention.
pub drop: crate::StableLike<unsafe extern "C" fn(&mut ()), core::num::NonZeroUsize>,
}
impl core::fmt::Debug for VtDrop {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "VtDrop({:p})", unsafe { self.drop.as_ref_unchecked() })
}
}
impl Hash for VtDrop {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.drop.hash(state)
}
}
impl PartialEq for VtDrop {
fn eq(&self, other: &Self) -> bool {
core::ptr::eq(
Expand All @@ -136,7 +265,7 @@ impl PartialEq for VtDrop {
}
}
impl<'a, T> IConstConstructor<'a, T> for VtDrop {
const VTABLE: &'a VtDrop = &VtDrop {
const VTABLE: VtDrop = VtDrop {
drop: unsafe {
core::mem::transmute({
unsafe extern "C" fn drop<T>(this: &mut T) {
Expand All @@ -150,7 +279,7 @@ impl<'a, T> IConstConstructor<'a, T> for VtDrop {

/// A marker for vtables for types that are `Send`
#[stabby::stabby]
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct VtSend<T>(pub T);
impl CompoundVt for dyn Send {
type Vt<T> = VtSend<T>;
Expand All @@ -166,18 +295,18 @@ impl<Head, Tail> From<crate::vtable::VtSend<VTable<Head, Tail>>> for VTable<Head
}
}
impl<'a, T: Send, Vt: IConstConstructor<'a, T>> IConstConstructor<'a, T> for VtSend<Vt> {
const VTABLE: &'a VtSend<Vt> = &VtSend(*Vt::VTABLE);
const VTABLE: VtSend<Vt> = VtSend(Vt::VTABLE);
}

/// A marker for vtables for types that are `Sync`
#[stabby::stabby]
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct VtSync<T>(pub T);
impl CompoundVt for dyn Sync {
type Vt<T> = VtSync<T>;
}
impl<'a, T: Sync, Vt: IConstConstructor<'a, T>> IConstConstructor<'a, T> for VtSync<Vt> {
const VTABLE: &'a VtSync<Vt> = &VtSync(*Vt::VTABLE);
const VTABLE: VtSync<Vt> = VtSync(Vt::VTABLE);
}
impl<Tail: TransitiveDeref<Vt, N>, Vt, N> TransitiveDeref<Vt, N> for VtSync<Tail> {
fn tderef(&self) -> &Vt {
Expand Down
16 changes: 15 additions & 1 deletion stabby-macros/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,8 @@ impl<'a> DynTraitDescription<'a> {
)*
}
};
let vtid_str = vtid.to_string();
let all_fn_ids_str = all_fn_ids.iter().map(|id| id.to_string());
vtable_decl = crate::stabby(proc_macro::TokenStream::new(), vtable_decl.into()).into();
quote! {
#[doc = #vt_doc]
Expand All @@ -669,6 +671,18 @@ impl<'a> DynTraitDescription<'a> {
#(core::ptr::eq((*unsafe{self.#all_fn_ids.as_ref_unchecked()}) as *const (), (*unsafe{other.#all_fn_ids.as_ref_unchecked()}) as *const _) &&)* true
}
}
impl< #vt_generics > core::hash::Hash for #vtid < #nbvt_generics > where #(#vt_bounds)*{
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
#(self.#all_fn_ids.hash(state);)*
}
}
impl< #vt_generics > core::fmt::Debug for #vtid < #nbvt_generics > where #(#vt_bounds)*{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut s = f.debug_struct(#vtid_str);
#(s.field(#all_fn_ids_str, &core::format_args!("{:p}", unsafe{self.#all_fn_ids.as_ref_unchecked()}));)*
s.finish()
}
}

impl<
'stabby_vt_lt, #(#trait_lts,)*
Expand All @@ -683,7 +697,7 @@ impl<'a> DynTraitDescription<'a> {
#(#unbound_trait_types: 'stabby_vt_lt,)*
#(#dyntrait_types: 'stabby_vt_lt,)* {
#[doc = #vt_doc]
const VTABLE: &'stabby_vt_lt #vt_signature = & #vtid {
const VTABLE: #vt_signature = #vtid {
#(#all_fn_ids: unsafe {core::mem::transmute(#self_as_trait::#all_fn_ids as #fn_ptrs)},)*
};
}
Expand Down

0 comments on commit a752895

Please sign in to comment.