Does this mean that my (A)RcBorrow::upgrade is unsound? It's a not too uncommon pattern to allow upgrading from &T where the T is statically known to be behind an (A)Rc to an (A)Rc<T>, and that's what that library is providing a type for.
The implementation currently uses
pub struct $RcBorrow<'a, T: ?Sized> {
raw: ptr::NonNull<T>,
marker: PhantomData<&'a $Rc<T>>
}
impl<'a, T: ?Sized> From<&'a $Rc<T>> for $RcBorrow<'a, T> {
fn from(v: &'a $Rc<T>) -> $RcBorrow<'a, T> {
$RcBorrow {
raw: (&**v).into(),
marker: PhantomData,
}
}
}
impl<'a, T: ?Sized> $RcBorrow<'a, T> {
/// Convert this borrowed pointer into an owned pointer.
pub fn upgrade(this: Self) -> $Rc<T> {
unsafe { $Rc::clone(&ManuallyDrop::new($Rc::from_raw(this.raw.as_ptr()))) }
}
/// Convert this borrowed pointer into a standard reference.
pub fn downgrade(this: Self) -> &'a T {
unsafe { &*this.raw.as_ptr() }
}
}
As I understand it, we've determined that this way of implementing into_raw is incorrect when using the current Stacked Borrows rules, so my "as_raw" implementation would also be unsound to turn into a &$Rc<T> to clone, as it's lost write provenance over the location of T. (And a unique $Rc<T> can be used to write.)
If I understand correctly, the way to make this sound would be to
- Provide a
$Rc::<T>::as_raw(&self) -> *const Tfunction that uses pointer manipulation instead ofDeref, - Use that in the implementation of
$RcBorrow<'_, T>, and - Say that the implicit case of
&T->$Rc<T>whereTis known statically to only be allocated behind$Rcis actually unsound (unsafe when used to write).
I'm ok with that for this library, but I know &T -> $Rc<T> is used in the wild.