Rollup merge of #155579 - Gaming32:fix-154998, r=Mark-Simulacrum

Make Rcs and Arcs use pointer comparison for unsized types

`Rc` and `Arc`s have an `Eq` implementation that first attempt to compare the pointers as an optimization. This, however, was not extended to DSTs, which is what this PR fixes.

Fixes rust-lang/rust#154998.
This commit is contained in:
Jacob Pratt
2026-04-26 21:56:40 -04:00
committed by GitHub
4 changed files with 61 additions and 5 deletions
+3 -3
View File
@@ -2615,7 +2615,7 @@ impl<T: ?Sized + PartialEq, A: Allocator> RcEqIdent<T, A> for Rc<T, A> {
#[rustc_unsafe_specialization_marker]
pub(crate) trait MarkerEq: PartialEq<Self> {}
impl<T: Eq> MarkerEq for T {}
impl<T: ?Sized + Eq> MarkerEq for T {}
/// We're doing this specialization here, and not as a more general optimization on `&T`, because it
/// would otherwise add a cost to all equality checks on refs. We assume that `Rc`s are used to
@@ -2628,12 +2628,12 @@ impl<T: Eq> MarkerEq for T {}
impl<T: ?Sized + MarkerEq, A: Allocator> RcEqIdent<T, A> for Rc<T, A> {
#[inline]
fn eq(&self, other: &Rc<T, A>) -> bool {
Rc::ptr_eq(self, other) || **self == **other
ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) || **self == **other
}
#[inline]
fn ne(&self, other: &Rc<T, A>) -> bool {
!Rc::ptr_eq(self, other) && **self != **other
!ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) && **self != **other
}
}
+2 -2
View File
@@ -3549,12 +3549,12 @@ impl<T: ?Sized + PartialEq, A: Allocator> ArcEqIdent<T, A> for Arc<T, A> {
impl<T: ?Sized + crate::rc::MarkerEq, A: Allocator> ArcEqIdent<T, A> for Arc<T, A> {
#[inline]
fn eq(&self, other: &Arc<T, A>) -> bool {
Arc::ptr_eq(self, other) || **self == **other
ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) || **self == **other
}
#[inline]
fn ne(&self, other: &Arc<T, A>) -> bool {
!Arc::ptr_eq(self, other) && **self != **other
!ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) && **self != **other
}
}
+28
View File
@@ -86,6 +86,34 @@ fn eq(&self, other: &TestEq) -> bool {
assert_eq!(*x.0.borrow(), 0);
}
#[test]
fn eq_unsized() {
#[derive(Eq)]
struct TestEq<T: ?Sized>(RefCell<usize>, T);
impl<T: ?Sized> PartialEq for TestEq<T> {
fn eq(&self, other: &TestEq<T>) -> bool {
*self.0.borrow_mut() += 1;
*other.0.borrow_mut() += 1;
true
}
}
let x = Arc::<TestEq<[u8; 3]>>::new(TestEq(RefCell::new(0), [0, 1, 2])) as Arc<TestEq<[u8]>>;
assert!(x == x);
assert!(!(x != x));
assert_eq!(*x.0.borrow(), 0);
}
#[test]
fn eq_unsized_slice() {
let a: Arc<[()]> = Arc::new([(); 3]);
let ptr: *const () = Arc::into_raw(a.clone()).cast();
let b: Arc<[()]> = unsafe { Arc::from_raw(core::ptr::slice_from_raw_parts(ptr, 42)) };
assert!(a == a);
assert!(!(a != a));
assert!(a != b);
assert!(!(a == b));
}
// The test code below is identical to that in `rc.rs`.
// For better maintainability we therefore define this type alias.
type Rc<T, A = std::alloc::Global> = Arc<T, A>;
+28
View File
@@ -87,6 +87,34 @@ fn eq(&self, other: &TestEq) -> bool {
assert_eq!(*x.0.borrow(), 0);
}
#[test]
fn eq_unsized() {
#[derive(Eq)]
struct TestEq<T: ?Sized>(RefCell<usize>, T);
impl<T: ?Sized> PartialEq for TestEq<T> {
fn eq(&self, other: &TestEq<T>) -> bool {
*self.0.borrow_mut() += 1;
*other.0.borrow_mut() += 1;
true
}
}
let x = Rc::<TestEq<[u8; 3]>>::new(TestEq(RefCell::new(0), [0, 1, 2])) as Rc<TestEq<[u8]>>;
assert!(x == x);
assert!(!(x != x));
assert_eq!(*x.0.borrow(), 0);
}
#[test]
fn eq_unsized_slice() {
let a: Rc<[()]> = Rc::new([(); 3]);
let ptr: *const () = Rc::into_raw(a.clone()).cast();
let b: Rc<[()]> = unsafe { Rc::from_raw(core::ptr::slice_from_raw_parts(ptr, 42)) };
assert!(a == a);
assert!(!(a != a));
assert!(a != b);
assert!(!(a == b));
}
const SHARED_ITER_MAX: u16 = 100;
fn assert_trusted_len<I: TrustedLen>(_: &I) {}