Skip to content

Commit

Permalink
Ignore vtables in {Rc, Arc, Weak}::ptr_eq
Browse files Browse the repository at this point in the history
We sometimes generate duplicate vtables depending on the number of
codegen units (rust-lang#46139), so we need to exclude the vtable part when
comparing fat pointers.

This is safe for the case of Rc, Arc, and Weak because these always
point to an entire allocation.  These methods can’t be used to
compare, e.g., a struct with one of its fields, a slice with one of
its subslices, or consecutive ZST fields.

Fixes rust-lang#63021.

Signed-off-by: Anders Kaseorg <[email protected]>
  • Loading branch information
andersk committed Jan 24, 2021
1 parent 9a9477f commit bda12a6
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 4 deletions.
8 changes: 6 additions & 2 deletions library/alloc/src/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,9 @@ impl<T: ?Sized> Rc<T> {
///
/// [`ptr::eq`]: core::ptr::eq
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
this.ptr.as_ptr() == other.ptr.as_ptr()
// The *const u8 cast discards the vtable to work around
// https://github.com/rust-lang/rust/issues/46139
this.ptr.as_ptr() as *const u8 == other.ptr.as_ptr() as *const u8
}
}

Expand Down Expand Up @@ -2136,7 +2138,9 @@ impl<T: ?Sized> Weak<T> {
#[inline]
#[stable(feature = "weak_ptr_eq", since = "1.39.0")]
pub fn ptr_eq(&self, other: &Self) -> bool {
self.ptr.as_ptr() == other.ptr.as_ptr()
// The *const u8 cast discards the vtable to work around
// https://github.com/rust-lang/rust/issues/46139
self.ptr.as_ptr() as *const u8 == other.ptr.as_ptr() as *const u8
}
}

Expand Down
8 changes: 6 additions & 2 deletions library/alloc/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,9 @@ impl<T: ?Sized> Arc<T> {
///
/// [`ptr::eq`]: core::ptr::eq
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
this.ptr.as_ptr() == other.ptr.as_ptr()
// The *const u8 cast discards the vtable to work around
// https://github.com/rust-lang/rust/issues/46139
this.ptr.as_ptr() as *const u8 == other.ptr.as_ptr() as *const u8
}
}

Expand Down Expand Up @@ -1936,7 +1938,9 @@ impl<T: ?Sized> Weak<T> {
#[inline]
#[stable(feature = "weak_ptr_eq", since = "1.39.0")]
pub fn ptr_eq(&self, other: &Self) -> bool {
self.ptr.as_ptr() == other.ptr.as_ptr()
// The *const u8 cast discards the vtable to work around
// https://github.com/rust-lang/rust/issues/46139
self.ptr.as_ptr() as *const u8 == other.ptr.as_ptr() as *const u8
}
}

Expand Down
17 changes: 17 additions & 0 deletions library/alloc/tests/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,20 @@ fn shared_from_iter_trustedlen_no_fuse() {
assert_trusted_len(&iter);
assert_eq!(&[Box::new(42), Box::new(24)], &*iter.collect::<Rc<[_]>>());
}

mod other_mod {
use std::sync::Arc;

pub fn cast(r: Arc<()>) -> Arc<dyn Send> {
r
}
}

#[test]
fn ptr_eq_ignores_duplicated_vtable() {
let a = Arc::new(());
let b = other_mod::cast(Arc::clone(&a));
let c: Arc<dyn Send> = a;
assert!(Arc::ptr_eq(&b, &c));
assert!(Weak::ptr_eq(&Arc::downgrade(&b), &Arc::downgrade(&c)));
}
17 changes: 17 additions & 0 deletions library/alloc/tests/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,20 @@ fn shared_from_iter_trustedlen_no_fuse() {
assert_trusted_len(&iter);
assert_eq!(&[Box::new(42), Box::new(24)], &*iter.collect::<Rc<[_]>>());
}

mod other_mod {
use std::rc::Rc;

pub fn cast(r: Rc<()>) -> Rc<dyn Send> {
r
}
}

#[test]
fn ptr_eq_ignores_duplicated_vtable() {
let a = Rc::new(());
let b = other_mod::cast(Rc::clone(&a));
let c: Rc<dyn Send> = a;
assert!(Rc::ptr_eq(&b, &c));
assert!(Weak::ptr_eq(&Rc::downgrade(&b), &Rc::downgrade(&c)));
}

0 comments on commit bda12a6

Please sign in to comment.