use core::{
mem::ManuallyDrop,
ptr,
};
pub fn lift<F, R>(root: R, fun: F) -> R
where
F: for<'a> FnOnce(&'a R) -> &'a mut R,
{
let root = ManuallyDrop::new(root);
let slot = fun(&root) as *mut R as *mut ManuallyDrop<R>;
unsafe { replace(slot, &root as *const _) }
}
pub fn lift_with<F, E, R>(root: R, extra: &E, fun: F) -> R
where
F: for<'a> FnOnce(&'a R, &'a E) -> &'a mut R,
{
let root = ManuallyDrop::new(root);
let slot = fun(&root, extra) as *mut R as *mut ManuallyDrop<R>;
unsafe { replace(slot, &root as *const _) }
}
pub fn lift_with_mut<F, E, R>(root: R, extra: &mut E, fun: F) -> R
where
F: for<'a> FnOnce(&'a R, &'a mut E) -> &'a mut R,
{
let root = ManuallyDrop::new(root);
let slot = fun(&root, extra) as *mut R as *mut ManuallyDrop<R>;
unsafe { replace(slot, &root as *const _) }
}
unsafe fn replace<T>(dest: *mut ManuallyDrop<T>, src: *const ManuallyDrop<T>) -> T {
let result = ptr::read(dest);
ptr::copy(src, dest, 1);
ManuallyDrop::into_inner(result)
}
#[cfg(test)]
mod tests {
use std::{cell, mem};
use super::*;
fn lift_self_impl(cell_ref: &cell::UnsafeCell<Box<i32>>) -> &mut cell::UnsafeCell<Box<i32>> {
let borrowed: &mut Box<i32> = unsafe { &mut *cell_ref.get() };
let transmuted: &mut cell::UnsafeCell<Box<i32>> = unsafe { mem::transmute(borrowed) };
transmuted
}
#[test]
fn lift_self() {
let cell = cell::UnsafeCell::new(Box::new(7));
lift(cell, lift_self_impl);
}
#[test]
fn lift_with_self() {
let cell = cell::UnsafeCell::new(Box::new(7));
lift_with(cell, &(), |cell_ref, _| lift_self_impl(cell_ref));
}
#[test]
fn lift_with_mut_self() {
let cell = cell::UnsafeCell::new(Box::new(7));
lift_with_mut(cell, &mut (), |cell_ref, _| lift_self_impl(cell_ref));
}
struct Struct;
#[allow(clippy::mut_from_ref)]
trait LeakBorrow {
fn foo(&self) -> &mut Box<dyn LeakBorrow>;
}
impl LeakBorrow for cell::RefCell<Box<dyn LeakBorrow>> {
fn foo(&self) -> &mut Box<dyn LeakBorrow> {
let refmut: cell::RefMut<'_, _> = self.borrow_mut();
let refmut_refmut: &mut cell::RefMut<'_, _> = Box::leak(Box::new(refmut));
will_leak(&*refmut_refmut);
&mut **refmut_refmut
}
}
impl LeakBorrow for Struct {
fn foo(&self) -> &mut Box<dyn LeakBorrow> {
unimplemented!()
}
}
#[test]
fn lift_leak_borrow() {
let root = Box::new(Struct) as Box<dyn LeakBorrow>;
let root = Box::new(cell::RefCell::new(root)) as Box<dyn LeakBorrow>;
lift(root, |b| b.foo());
}
#[test]
fn lift_with_leak_borrow() {
let root = Box::new(Struct) as Box<dyn LeakBorrow>;
let root = Box::new(cell::RefCell::new(root)) as Box<dyn LeakBorrow>;
lift_with(root, &(), |b, _| b.foo());
}
#[test]
fn lift_with_mut_leak_borrow() {
let root = Box::new(Struct) as Box<dyn LeakBorrow>;
let root = Box::new(cell::RefCell::new(root)) as Box<dyn LeakBorrow>;
lift_with_mut(root, &mut (), |b, _| b.foo());
}
fn will_leak<T>(_t: &T) {
#[cfg(miri)]
{
unsafe { miri_static_root(_t as *const _ as *const u8) };
}
}
#[cfg(miri)]
extern "Rust" {
fn miri_static_root(ptr: *const u8);
}
}