#![no_std]
#![deny(missing_docs)]
#![allow(unused_unsafe)] use core::{
alloc::Layout,
future::Future,
marker::PhantomData,
};
mod impls {
use core::ptr::NonNull;
use super::CoerciblePtr;
unsafe impl<T, U: ?Sized> CoerciblePtr<U> for *const T {
type Pointee = T;
type Output = *const U;
fn as_sized_ptr(self: &mut *const T) -> *mut T {
(*self) as *const T as *mut T
}
unsafe fn replace_ptr(self, new: *mut U) -> *const U {
super::unsize_with(self as *mut T, |_| new)
}
}
unsafe impl<T, U: ?Sized> CoerciblePtr<U> for *mut T {
type Pointee = T;
type Output = *mut U;
fn as_sized_ptr(self: &mut *mut T) -> *mut T {
*self
}
unsafe fn replace_ptr(self, new: *mut U) -> *mut U {
super::unsize_with(self, |_| new)
}
}
unsafe impl<'lt, T, U: ?Sized + 'lt> CoerciblePtr<U> for &'lt T {
type Pointee = T;
type Output = &'lt U;
fn as_sized_ptr(self: &mut &'lt T) -> *mut T {
(*self) as *const T as *mut T
}
unsafe fn replace_ptr(self, new: *mut U) -> &'lt U {
unsafe { &*super::unsize_with(self as *const T as *mut T, |_| new) }
}
}
unsafe impl<'lt, T, U: ?Sized + 'lt> CoerciblePtr<U> for &'lt mut T {
type Pointee = T;
type Output = &'lt mut U;
fn as_sized_ptr(self: &mut &'lt mut T) -> *mut T {
&mut **self
}
unsafe fn replace_ptr(self, new: *mut U) -> &'lt mut U {
unsafe { &mut *super::unsize_with(self, |_| new) }
}
}
unsafe impl<Ptr, U : ?Sized, T> CoerciblePtr<U> for core::pin::Pin<Ptr>
where
Ptr: CoerciblePtr<U, Pointee=T> + core::ops::DerefMut<Target=T>,
Ptr::Output: core::ops::DerefMut<Target=U>,
{
type Pointee = T;
type Output = core::pin::Pin<Ptr::Output>;
fn as_sized_ptr(&mut self) -> *mut Self::Pointee {
unsafe { self.as_mut().get_unchecked_mut() }
}
unsafe fn replace_ptr(self, new: *mut U) -> Self::Output {
let inner = core::pin::Pin::into_inner_unchecked(self);
let new = inner.replace_ptr(new);
core::pin::Pin::new_unchecked(new)
}
}
unsafe impl<T, U: ?Sized> CoerciblePtr<U> for core::ptr::NonNull<T> {
type Pointee = T;
type Output = NonNull<U>;
fn as_sized_ptr(&mut self) -> *mut T {
self.as_ptr()
}
unsafe fn replace_ptr(self, new: *mut U) -> NonNull<U> {
NonNull::new_unchecked(new)
}
}
}
#[repr(C)]
pub struct Coercion<T, U : ?Sized, F : FnOnce(*const T) -> *const U = fn(*const T) -> *const U> {
pub(crate) coerce: F,
pub(crate) _phantom: PhantomData<fn(*const T) -> *const U>,
}
mod coercion_impls;
impl<F, T, U: ?Sized> Coercion<T, U, F>
where
F : FnOnce(*const T) -> *const U,
{
pub unsafe fn new(coerce: F) -> Self {
Coercion { coerce, _phantom: PhantomData }
}
}
macro_rules! coerce_to_dyn_trait {
(
$(for <$($generics:ident),* $(,)?>)?
$(#[$attr:meta])* fn $name:ident() -> $trait_type:path
) => {
impl<'lt, T: $trait_type + 'lt, $($($generics),*)?>
Coercion<T, dyn $trait_type + 'lt>
{
$(#[$attr])*
pub fn $name() -> Self {
fn coerce_to_that_type<'lt, T: $trait_type + 'lt, $($($generics),*)?>(
ptr: *const T
) -> *const (dyn $trait_type + 'lt) {
ptr
}
unsafe { Coercion::new(coerce_to_that_type) }
}
}
};
}
coerce_to_dyn_trait!(
fn to_any() -> core::any::Any
);
coerce_to_dyn_trait!(
fn to_debug() -> core::fmt::Debug
);
coerce_to_dyn_trait!(
fn to_display() -> core::fmt::Display
);
#[cfg(rustc_1_51)]
impl<T, const N: usize> Coercion<[T; N], [T]> {
pub fn to_slice() -> Self {
fn coerce<T, const N: usize>(
ptr: *const [T; N]
) -> *const [T] { ptr }
unsafe { Coercion::new(coerce) }
}
}
macro_rules! coerce_to_dyn_fn {
(
$(#![$attr:meta])?
$($arg:ident),*
) => {
coerce_to_dyn_fn!(
$(#![$attr])?
@<$($arg,)*>:
(dyn Fn($($arg,)*) -> T + 'lt),
(dyn FnMut($($arg,)*) -> T + 'lt),
(dyn FnOnce($($arg,)*) -> T + 'lt)
);
};
(
$(#![$attr:meta])?
@<$($arg:ident,)*>: $dyn:ty, $dyn_mut:ty, $dyn_once:ty
) => {
coerce_to_dyn_trait! { for<Ret, $($arg),*>
$(#[$attr])?
fn to_fn() -> Fn($($arg),*) -> Ret
}
coerce_to_dyn_trait! { for<Ret, $($arg),*>
$(#[$attr])?
fn to_fn_mut() -> FnMut($($arg),*) -> Ret
}
coerce_to_dyn_trait! { for<Ret, $($arg),*>
$(#[$attr])?
fn to_fn_once() -> FnOnce($($arg),*) -> Ret
}
};
}
coerce_to_dyn_fn!(#![doc(hidden)] );
coerce_to_dyn_fn!(#![doc(hidden)] A);
coerce_to_dyn_fn!(#![doc(hidden)] A,B);
coerce_to_dyn_fn!(#![doc(hidden)] A,B,C);
coerce_to_dyn_fn!(#![doc(hidden)] A,B,C,D);
coerce_to_dyn_fn!(#![doc(hidden)] A,B,C,D,E);
coerce_to_dyn_fn!(A,B,C,D,E,G);
coerce_to_dyn_trait! {
for<I,>
fn to_iterator() -> Iterator<Item=I>
}
coerce_to_dyn_trait! {
for<I,>
fn to_future() -> Future<Output=I>
}
extern {}
pub unsafe trait CoerciblePtr<U: ?Sized>: Sized {
type Pointee;
type Output;
fn as_sized_ptr(&mut self) -> *mut Self::Pointee;
unsafe fn replace_ptr(self, _: *mut U) -> Self::Output;
}
pub trait CoerceUnsize<U: ?Sized>: CoerciblePtr<U> {
fn unsize<F>(mut self, with: Coercion<Self::Pointee, U, F>) -> Self::Output
where
F : FnOnce(*const Self::Pointee) -> *const U,
{
unsafe {
let ptr = self.as_sized_ptr();
let new_ptr = unsize_with(ptr, with.coerce);
self.replace_ptr(new_ptr)
}
}
}
impl<T, U: ?Sized> CoerceUnsize<U> for T
where
T: CoerciblePtr<U>
{}
unsafe fn unsize_with<T, U: ?Sized>(
ptr: *mut T,
with: impl FnOnce(*const T) -> *const U,
) -> *mut U {
let mut raw_unsized = with(ptr) as *mut U;
assert_eq!(Layout::for_value(&raw_unsized), Layout::new::<[usize; 2]>(),
"Unexpected layout of unsized pointer.");
debug_assert_eq!(raw_unsized as *const u8 as usize, ptr as usize,
"Unsize coercion seemingly changed the pointer base");
let ptr_slot = &mut raw_unsized as *mut *mut U as *mut *const u8;
unsafe { ptr_slot.write(ptr as *const u8) };
raw_unsized
}
extern {}
#[cfg(test)]
mod tests;
#[macro_export]
macro_rules! Coercion {
(to dyn $($bounds:tt)*) => (
#[allow(unused_unsafe)] unsafe {
$crate::Coercion::new({
#[allow(unused_parens)]
fn coerce<'lt> (p: *const (impl $($bounds)* + 'lt)) -> *const (dyn $($bounds)* + 'lt) {
p
}
coerce
})
}
);
}