Skip to content

Commit

Permalink
Merge pull request #162 from andersk/dispatch-from-dyn
Browse files Browse the repository at this point in the history
Allow trait objects with methods on `self: Gc<Self>` on nightly
  • Loading branch information
Manishearth authored Jan 27, 2023
2 parents a243a10 + 4aad4f8 commit de3769d
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 35 deletions.
8 changes: 6 additions & 2 deletions gc/src/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl GcBoxHeader {
}

#[repr(C)] // to justify the layout computations in GcBox::from_box, Gc::from_raw
pub(crate) struct GcBox<T: Trace + ?Sized + 'static> {
pub(crate) struct GcBox<T: ?Sized + 'static> {
header: GcBoxHeader,
data: T,
}
Expand Down Expand Up @@ -217,22 +217,26 @@ unsafe fn insert_gcbox(gcbox: NonNull<GcBox<dyn Trace>>) {
});
}

impl<T: Trace + ?Sized> GcBox<T> {
impl<T: ?Sized> GcBox<T> {
/// Returns `true` if the two references refer to the same `GcBox`.
pub(crate) fn ptr_eq(this: &GcBox<T>, other: &GcBox<T>) -> bool {
// Use .header to ignore fat pointer vtables, to work around
// https://github.com/rust-lang/rust/issues/46139
ptr::eq(&this.header, &other.header)
}
}

impl<T: Trace + ?Sized> GcBox<T> {
/// Marks this `GcBox` and marks through its data.
pub(crate) unsafe fn trace_inner(&self) {
if !self.header.is_marked() {
self.header.mark();
self.data.trace();
}
}
}

impl<T: ?Sized> GcBox<T> {
/// Increases the root count on this `GcBox`.
/// Roots prevent the `GcBox` from being destroyed by the garbage collector.
pub(crate) unsafe fn root_inner(&self) {
Expand Down
76 changes: 44 additions & 32 deletions gc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
//! It is marked as non-sendable because the garbage collection only occurs
//! thread-locally.

#![cfg_attr(feature = "nightly", feature(coerce_unsized, unsize))]
#![cfg_attr(
feature = "nightly",
feature(coerce_unsized, dispatch_from_dyn, unsize)
)]

use crate::gc::{GcBox, GcBoxHeader};
use std::alloc::Layout;
Expand All @@ -21,7 +24,7 @@ use std::rc::Rc;
#[cfg(feature = "nightly")]
use std::marker::Unsize;
#[cfg(feature = "nightly")]
use std::ops::CoerceUnsized;
use std::ops::{CoerceUnsized, DispatchFromDyn};

mod gc;
#[cfg(feature = "serde")]
Expand All @@ -48,13 +51,16 @@ pub use crate::gc::{stats, GcStats};
/// A garbage-collected pointer type over an immutable value.
///
/// See the [module level documentation](./) for more details.
pub struct Gc<T: Trace + ?Sized + 'static> {
pub struct Gc<T: ?Sized + 'static> {
ptr_root: Cell<NonNull<GcBox<T>>>,
marker: PhantomData<Rc<T>>,
}

#[cfg(feature = "nightly")]
impl<T: Trace + ?Sized + Unsize<U>, U: Trace + ?Sized> CoerceUnsized<Gc<U>> for Gc<T> {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Gc<U>> for Gc<T> {}

#[cfg(feature = "nightly")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Gc<U>> for Gc<T> {}

impl<T: Trace> Gc<T> {
/// Constructs a new `Gc<T>` with the given value.
Expand Down Expand Up @@ -99,23 +105,23 @@ impl<T: Trace + ?Sized> Gc<T> {
}
}

impl<T: Trace + ?Sized> Gc<T> {
impl<T: ?Sized> Gc<T> {
/// Returns `true` if the two `Gc`s point to the same allocation.
pub fn ptr_eq(this: &Gc<T>, other: &Gc<T>) -> bool {
GcBox::ptr_eq(this.inner(), other.inner())
}
}

/// Returns the given pointer with its root bit cleared.
unsafe fn clear_root_bit<T: ?Sized + Trace>(ptr: NonNull<GcBox<T>>) -> NonNull<GcBox<T>> {
unsafe fn clear_root_bit<T: ?Sized>(ptr: NonNull<GcBox<T>>) -> NonNull<GcBox<T>> {
let ptr = ptr.as_ptr();
let data = ptr.cast::<u8>();
let addr = data as isize;
let ptr = set_data_ptr(ptr, data.wrapping_offset((addr & !1) - addr));
NonNull::new_unchecked(ptr)
}

impl<T: Trace + ?Sized> Gc<T> {
impl<T: ?Sized> Gc<T> {
fn rooted(&self) -> bool {
self.ptr_root.get().as_ptr().cast::<u8>() as usize & 1 != 0
}
Expand Down Expand Up @@ -151,7 +157,7 @@ impl<T: Trace + ?Sized> Gc<T> {
}
}

impl<T: Trace + ?Sized> Gc<T> {
impl<T: ?Sized> Gc<T> {
/// Consumes the `Gc`, returning the wrapped pointer.
///
/// To avoid a memory leak, the pointer must be converted back into a `Gc`
Expand Down Expand Up @@ -225,7 +231,7 @@ impl<T: Trace + ?Sized> Gc<T> {
}
}

impl<T: Trace + ?Sized> Finalize for Gc<T> {}
impl<T: ?Sized> Finalize for Gc<T> {}

unsafe impl<T: Trace + ?Sized> Trace for Gc<T> {
#[inline]
Expand Down Expand Up @@ -263,7 +269,7 @@ unsafe impl<T: Trace + ?Sized> Trace for Gc<T> {
}
}

impl<T: Trace + ?Sized> Clone for Gc<T> {
impl<T: ?Sized> Clone for Gc<T> {
#[inline]
fn clone(&self) -> Self {
unsafe {
Expand All @@ -278,7 +284,7 @@ impl<T: Trace + ?Sized> Clone for Gc<T> {
}
}

impl<T: Trace + ?Sized> Deref for Gc<T> {
impl<T: ?Sized> Deref for Gc<T> {
type Target = T;

#[inline]
Expand All @@ -287,7 +293,7 @@ impl<T: Trace + ?Sized> Deref for Gc<T> {
}
}

impl<T: Trace + ?Sized> Drop for Gc<T> {
impl<T: ?Sized> Drop for Gc<T> {
#[inline]
fn drop(&mut self) {
// If this pointer was a root, we should unroot it.
Expand All @@ -306,16 +312,16 @@ impl<T: Trace + Default> Default for Gc<T> {
}
}

impl<T: Trace + ?Sized + PartialEq> PartialEq for Gc<T> {
impl<T: ?Sized + PartialEq> PartialEq for Gc<T> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}

impl<T: Trace + ?Sized + Eq> Eq for Gc<T> {}
impl<T: ?Sized + Eq> Eq for Gc<T> {}

impl<T: Trace + ?Sized + PartialOrd> PartialOrd for Gc<T> {
impl<T: ?Sized + PartialOrd> PartialOrd for Gc<T> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(&**other)
Expand All @@ -342,32 +348,32 @@ impl<T: Trace + ?Sized + PartialOrd> PartialOrd for Gc<T> {
}
}

impl<T: Trace + ?Sized + Ord> Ord for Gc<T> {
impl<T: ?Sized + Ord> Ord for Gc<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}

impl<T: Trace + ?Sized + Hash> Hash for Gc<T> {
impl<T: ?Sized + Hash> Hash for Gc<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}

impl<T: Trace + ?Sized + Display> Display for Gc<T> {
impl<T: ?Sized + Display> Display for Gc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&**self, f)
}
}

impl<T: Trace + ?Sized + Debug> Debug for Gc<T> {
impl<T: ?Sized + Debug> Debug for Gc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&**self, f)
}
}

impl<T: Trace + ?Sized> fmt::Pointer for Gc<T> {
impl<T: ?Sized> fmt::Pointer for Gc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.inner(), f)
}
Expand All @@ -392,13 +398,13 @@ impl<
}
}

impl<T: Trace + ?Sized> std::borrow::Borrow<T> for Gc<T> {
impl<T: ?Sized> std::borrow::Borrow<T> for Gc<T> {
fn borrow(&self) -> &T {
self
}
}

impl<T: Trace + ?Sized> std::convert::AsRef<T> for Gc<T> {
impl<T: ?Sized> std::convert::AsRef<T> for Gc<T> {
fn as_ref(&self) -> &T {
self
}
Expand Down Expand Up @@ -491,7 +497,7 @@ pub struct GcCell<T: ?Sized + 'static> {
cell: UnsafeCell<T>,
}

impl<T: Trace> GcCell<T> {
impl<T> GcCell<T> {
/// Creates a new `GcCell` containing `value`.
#[inline]
pub fn new(value: T) -> Self {
Expand All @@ -508,7 +514,7 @@ impl<T: Trace> GcCell<T> {
}
}

impl<T: Trace + ?Sized> GcCell<T> {
impl<T: ?Sized> GcCell<T> {
/// Immutably borrows the wrapped value.
///
/// The borrow lasts until the returned `GcCellRef` exits scope.
Expand All @@ -524,7 +530,9 @@ impl<T: Trace + ?Sized> GcCell<T> {
Err(e) => panic!("{}", e),
}
}
}

impl<T: Trace + ?Sized> GcCell<T> {
/// Mutably borrows the wrapped value.
///
/// The borrow lasts until the returned `GcCellRefMut` exits scope.
Expand All @@ -540,7 +548,9 @@ impl<T: Trace + ?Sized> GcCell<T> {
Err(e) => panic!("{}", e),
}
}
}

impl<T: ?Sized> GcCell<T> {
/// Immutably borrows the wrapped value, returning an error if the value is currently mutably
/// borrowed.
///
Expand Down Expand Up @@ -583,7 +593,9 @@ impl<T: Trace + ?Sized> GcCell<T> {
})
}
}
}

impl<T: Trace + ?Sized> GcCell<T> {
/// Mutably borrows the wrapped value, returning an error if the value is currently borrowed.
///
/// The borrow lasts until the returned `GcCellRefMut` exits scope.
Expand Down Expand Up @@ -646,7 +658,7 @@ impl std::fmt::Display for BorrowMutError {
}
}

impl<T: Trace + ?Sized> Finalize for GcCell<T> {}
impl<T: ?Sized> Finalize for GcCell<T> {}

unsafe impl<T: Trace + ?Sized> Trace for GcCell<T> {
#[inline]
Expand Down Expand Up @@ -926,30 +938,30 @@ impl<'a, T: Trace + ?Sized, U: Display + ?Sized> Display for GcCellRefMut<'a, T,

unsafe impl<T: ?Sized + Send> Send for GcCell<T> {}

impl<T: Trace + Clone> Clone for GcCell<T> {
impl<T: Clone> Clone for GcCell<T> {
#[inline]
fn clone(&self) -> Self {
Self::new(self.borrow().clone())
}
}

impl<T: Trace + Default> Default for GcCell<T> {
impl<T: Default> Default for GcCell<T> {
#[inline]
fn default() -> Self {
Self::new(Default::default())
}
}

impl<T: Trace + ?Sized + PartialEq> PartialEq for GcCell<T> {
impl<T: ?Sized + PartialEq> PartialEq for GcCell<T> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
*self.borrow() == *other.borrow()
}
}

impl<T: Trace + ?Sized + Eq> Eq for GcCell<T> {}
impl<T: ?Sized + Eq> Eq for GcCell<T> {}

impl<T: Trace + ?Sized + PartialOrd> PartialOrd for GcCell<T> {
impl<T: ?Sized + PartialOrd> PartialOrd for GcCell<T> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(*self.borrow()).partial_cmp(&*other.borrow())
Expand All @@ -976,14 +988,14 @@ impl<T: Trace + ?Sized + PartialOrd> PartialOrd for GcCell<T> {
}
}

impl<T: Trace + ?Sized + Ord> Ord for GcCell<T> {
impl<T: ?Sized + Ord> Ord for GcCell<T> {
#[inline]
fn cmp(&self, other: &GcCell<T>) -> Ordering {
(*self.borrow()).cmp(&*other.borrow())
}
}

impl<T: Trace + ?Sized + Debug> Debug for GcCell<T> {
impl<T: ?Sized + Debug> Debug for GcCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.flags.get().borrowed() {
BorrowState::Unused | BorrowState::Reading => f
Expand Down
2 changes: 1 addition & 1 deletion gc/src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ impl<'de, T: Deserialize<'de> + Trace> Deserialize<'de> for Gc<T> {
}
}

impl<T: Serialize + Trace> Serialize for Gc<T> {
impl<T: Serialize> Serialize for Gc<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
Expand Down
21 changes: 21 additions & 0 deletions gc/tests/gc_self_method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#![cfg(feature = "nightly")]
#![feature(arbitrary_self_types)]

use gc::{Finalize, Gc, Trace};

trait Foo: Trace {
fn foo(self: Gc<Self>);
}

#[derive(Trace, Finalize)]
struct Bar;

impl Foo for Bar {
fn foo(self: Gc<Bar>) {}
}

#[test]
fn gc_self_method() {
let gc: Gc<dyn Foo> = Gc::new(Bar);
gc.foo();
}

0 comments on commit de3769d

Please sign in to comment.