Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow trait objects with methods on self: Gc<Self> on nightly #162

Merged
merged 2 commits into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
}