use core::mem;
use core::ops::Deref;
use core::ops::DerefMut;
use core::pin::Pin;
use core::ptr;
#[cfg(doc)]
use {
crate::{drop_flag, moveit},
alloc::{boxed::Box, rc::Rc, sync::Arc},
core::mem::{ManuallyDrop, MaybeUninit},
};
use crate::drop_flag::DropFlag;
use crate::slot::DroppingSlot;
pub struct MoveRef<'a, T: ?Sized> {
ptr: &'a mut T,
drop_flag: DropFlag<'a>,
}
impl<'a, T: ?Sized> MoveRef<'a, T> {
#[inline]
pub unsafe fn new_unchecked(ptr: &'a mut T, drop_flag: DropFlag<'a>) -> Self {
Self { ptr, drop_flag }
}
#[inline]
pub fn into_pin(this: Self) -> Pin<Self> {
unsafe { Pin::new_unchecked(this) }
}
#[inline]
pub fn as_ptr(this: &Self) -> *const T {
this.ptr
}
#[inline]
pub fn as_mut_ptr(this: &mut Self) -> *mut T {
this.ptr
}
#[allow(unused)]
pub(crate) fn drop_flag(this: &Self) -> DropFlag<'a> {
this.drop_flag
}
}
impl<'a, T> MoveRef<'a, T> {
pub(crate) unsafe fn cast<U>(mut self) -> MoveRef<'a, U> {
let mr = MoveRef {
ptr: &mut *Self::as_mut_ptr(&mut self).cast(),
drop_flag: self.drop_flag,
};
mem::forget(self);
mr
}
}
impl<'a, T> MoveRef<'a, T> {
#[inline]
pub fn into_inner(this: Self) -> T {
unsafe {
let val = ptr::read(this.ptr);
let _ = this.cast::<()>();
val
}
}
}
impl<T: ?Sized> Deref for MoveRef<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
self.ptr
}
}
impl<T: ?Sized> DerefMut for MoveRef<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.ptr
}
}
impl<T: ?Sized> Drop for MoveRef<'_, T> {
#[inline]
fn drop(&mut self) {
self.drop_flag.dec_and_check_if_died();
unsafe { ptr::drop_in_place(self.ptr) }
}
}
impl<'a, T> From<MoveRef<'a, T>> for Pin<MoveRef<'a, T>> {
#[inline]
fn from(x: MoveRef<'a, T>) -> Self {
MoveRef::into_pin(x)
}
}
pub trait AsMove: Deref + Sized {
type Storage: Sized;
fn as_move<'frame>(
self,
storage: DroppingSlot<'frame, Self::Storage>,
) -> Pin<MoveRef<'frame, Self::Target>>
where
Self: 'frame;
}
impl<'f, T: ?Sized> AsMove for MoveRef<'f, T> {
type Storage = ();
#[inline]
fn as_move<'frame>(
self,
storage: DroppingSlot<'frame, Self::Storage>,
) -> Pin<MoveRef<'frame, Self::Target>>
where
Self: 'frame,
{
MoveRef::into_pin(DerefMove::deref_move(self, storage))
}
}
impl<P: DerefMove> AsMove for Pin<P> {
type Storage = P::Storage;
#[inline]
fn as_move<'frame>(
self,
storage: DroppingSlot<'frame, Self::Storage>,
) -> Pin<MoveRef<'frame, Self::Target>>
where
Self: 'frame,
{
unsafe {
MoveRef::into_pin(P::deref_move(Pin::into_inner_unchecked(self), storage))
}
}
}
pub unsafe trait DerefMove: DerefMut + AsMove {
fn deref_move<'frame>(
self,
storage: DroppingSlot<'frame, Self::Storage>,
) -> MoveRef<'frame, Self::Target>
where
Self: 'frame;
}
unsafe impl<'a, T: ?Sized> DerefMove for MoveRef<'a, T> {
#[inline]
fn deref_move<'frame>(
self,
_storage: DroppingSlot<'frame, Self::Storage>,
) -> MoveRef<'frame, Self::Target>
where
Self: 'frame,
{
self
}
}
unsafe impl<P> DerefMove for Pin<P>
where
P: DerefMove, P::Target: Unpin, {
#[inline]
fn deref_move<'frame>(
self,
storage: DroppingSlot<'frame, Self::Storage>,
) -> MoveRef<'frame, Self::Target>
where
Self: 'frame,
{
Pin::into_inner(self.as_move(storage))
}
}
#[doc(hidden)]
pub mod __macro {
use super::*;
use core::marker::PhantomData;
pub struct DerefPhantom<T>(PhantomData<*const T>);
impl<T: DerefMove> DerefPhantom<T> {
#[inline]
pub fn new(_: &T) -> Self {
Self(PhantomData)
}
#[inline]
pub fn deref_move<'frame>(
self,
this: T,
storage: DroppingSlot<'frame, T::Storage>,
) -> MoveRef<'frame, T::Target>
where
Self: 'frame,
{
T::deref_move(this, storage)
}
}
}
#[macro_export]
macro_rules! moveit {
(let $name:ident $(: $ty:ty)? = &move *$expr:expr $(; $($rest:tt)*)?) => {
$crate::moveit!(@move $name, $($ty)?, $expr);
$crate::moveit!($($($rest)*)?);
};
(let mut $name:ident $(: $ty:ty)? = &move *$expr:expr $(; $($rest:tt)*)?) => {
$crate::moveit!(@move(mut) $name, $($ty)?, $expr);
$crate::moveit!($($($rest)*)?);
};
(let $name:ident $(: $ty:ty)? = &move $expr:expr $(; $($rest:tt)*)?) => {
$crate::moveit!(@put $name, $($ty)?, $expr);
$crate::moveit!($($($rest)*)?);
};
(let mut $name:ident $(: $ty:ty)? = &move $expr:expr $(; $($rest:tt)*)?) => {
$crate::emplace!(@put(mut) $name, $($ty)?, $expr);
$crate::emplace!($($($rest)*)?);
};
(let $name:ident $(: $ty:ty)? = $expr:expr $(; $($rest:tt)*)?) => {
$crate::moveit!(@emplace $name, $($ty)?, $expr);
$crate::moveit!($($($rest)*)?);
};
(let mut $name:ident $(: $ty:ty)? = $expr:expr $(; $($rest:tt)*)?) => {
$crate::moveit!(@emplace(mut) $name, $($ty)?, $expr);
$crate::moveit!($($($rest)*)?);
};
($(;)?) => {};
(&move *$expr:expr) => {
$crate::move_ref::DerefMove::deref_move(
$expr, $crate::slot!(#[dropping]),
)
};
(&move $expr:expr) => {$crate::slot!().put($expr)};
($expr:expr) => {$crate::slot!().emplace($expr)};
(@move $(($mut:tt))? $name:ident, $($ty:ty)?, $expr:expr) => {
$crate::slot!(#[dropping] storage);
#[allow(unused_mut)]
let $($mut)? $name $(: $ty)? = $crate::move_ref::DerefMove::deref_move($expr, storage);
};
(@put $(($mut:tt))? $name:ident, $($ty:ty)?, $expr:expr) => {
$crate::slot!(slot);
let $($mut)? $name $(: $ty)? = slot.put($expr);
};
(@emplace $(($mut:tt))? $name:ident, $($ty:ty)?, $expr:expr) => {
$crate::slot!(slot);
let $($mut)? $name $(: $ty)? = slot.emplace($expr);
};
}
#[cfg(test)]
pub(crate) mod test {
use crate::new;
use crate::MoveNew;
use crate::New;
use super::*;
use std::alloc;
use std::alloc::Layout;
use std::marker::PhantomPinned;
use std::mem::MaybeUninit;
#[test]
fn deref_move_of_move_ref() {
moveit! {
let x: MoveRef<Box<i32>> = &move Box::new(5);
let y: MoveRef<Box<i32>> = &move *x;
}
let _ = y;
}
#[test]
fn deref_move_of_box() {
let x = Box::new(5);
moveit!(let y: MoveRef<i32> = &move *x);
let _ = y;
}
#[test]
fn move_ref_into_inner() {
moveit!(let x: MoveRef<Box<i32>> = &move Box::new(5));
let _ = MoveRef::into_inner(x);
}
#[test]
#[should_panic]
fn unforgettable() {
moveit!(let x: MoveRef<i32> = &move 42);
mem::forget(x);
}
#[test]
#[should_panic]
fn unforgettable_temporary() {
mem::forget(moveit!(&move 42));
}
#[test]
fn forgettable_box() {
let mut x = Box::new(5);
let ptr = x.as_mut() as *mut i32;
moveit!(let y: MoveRef<i32> = &move *x);
mem::forget(y);
unsafe {
alloc::dealloc(ptr as *mut u8, Layout::new::<i32>());
}
}
#[test]
fn forgettable_box_temporary() {
let mut x = Box::new(5);
let ptr = x.as_mut() as *mut i32;
mem::forget(moveit!(&move *x));
unsafe {
alloc::dealloc(ptr as *mut u8, Layout::new::<i32>());
}
}
#[derive(Default)]
pub(crate) struct Immovable {
_pin: PhantomPinned,
}
impl Immovable {
pub(crate) fn new() -> impl New<Output = Self> {
new::default()
}
}
unsafe impl MoveNew for Immovable {
unsafe fn move_new(
_src: Pin<MoveRef<Self>>,
_this: Pin<&mut MaybeUninit<Self>>,
) {
}
}
#[test]
fn test_mov() {
moveit! {
let foo = Immovable::new();
let _foo = new::mov(foo);
}
}
}