[go: up one dir, main page]

unsize/
lib.rs

1//! This module encapsulates correct coercers and coercible pointers.
2//!
3//! The act of Coercion, a special kind of somewhat trivial pointer conversion, is not exposed as a
4//! _safe_ trait. There are two unsafe traits: The first captures which structs unsized to which
5//! dynamically sized types; which the second is applied to (wrappers around) pointers that can be
6//! coerced implicitly.
7//!
8//! We do not have the luxury of compiler builtin checks to enforce that a particular pointer
9//! conversion is sound, nor can we generate the tag of target fat pointer from thin air. Instead,
10//! we use a trick. Note that both of these safety issues occur in the context of the pointer
11//! conversion. Now, we can require the _user_ to _unsafely_ provide a function that implements the
12//! conversion correctly. The _using_ this function is safe and enables any particular user-defined
13//! pointer wrapper to safely transform itself. Note that for a limited selection of standard
14//! traits we can even go so far as offer pre-built converters that are safe to use in general.
15// Copyright 2019-2021 Andreas Molzer
16#![no_std]
17#![deny(missing_docs)]
18
19#![allow(unused_unsafe)] // Err on the side of caution.
20use core::{
21    alloc::Layout,
22    future::Future,
23    marker::PhantomData,
24};
25
26mod impls {
27    //! Safety: Provenance is always the same as self, pointer target is simply passed through.
28    use core::ptr::NonNull;
29    use super::CoerciblePtr;
30
31    unsafe impl<T, U: ?Sized> CoerciblePtr<U> for *const T {
32        type Pointee = T;
33        type Output = *const U;
34        fn as_sized_ptr(self: &mut *const T) -> *mut T {
35            (*self) as *const T as *mut T
36        }
37        unsafe fn replace_ptr(self, new: *mut U) -> *const U {
38            // See the mutable version.
39            super::unsize_with(self as *mut T, |_| new)
40        }
41    }
42
43    unsafe impl<T, U: ?Sized> CoerciblePtr<U> for *mut T {
44        type Pointee = T;
45        type Output = *mut U;
46        fn as_sized_ptr(self: &mut *mut T) -> *mut T {
47            *self
48        }
49        unsafe fn replace_ptr(self, new: *mut U) -> *mut U {
50            // See the mutable version.
51            super::unsize_with(self, |_| new)
52        }
53    }
54
55    unsafe impl<'lt, T, U: ?Sized + 'lt> CoerciblePtr<U> for &'lt T {
56        type Pointee = T;
57        type Output = &'lt U;
58        fn as_sized_ptr(self: &mut &'lt T) -> *mut T {
59            (*self) as *const T as *mut T
60        }
61        unsafe fn replace_ptr(self, new: *mut U) -> &'lt U {
62            // See the mutable version.
63            unsafe { &*super::unsize_with(self as *const T as *mut T, |_| new) }
64        }
65    }
66
67    /// Safety: Provenance is always the same as self.
68    unsafe impl<'lt, T, U: ?Sized + 'lt> CoerciblePtr<U> for &'lt mut T {
69        type Pointee = T;
70        type Output = &'lt mut U;
71        fn as_sized_ptr(self: &mut &'lt mut T) -> *mut T {
72            &mut **self
73        }
74        unsafe fn replace_ptr(self, new: *mut U) -> &'lt mut U {
75            // (Explanation should apply to the const version too).
76            // We want the `new` pointer with provenance of `self`. This is because in
77            // `as_sized_ptr` we had only borrowed the mutably reference and the usage of passing
78            // it as argument to this method has invalidated this borrow.
79            // We reuse `unsize_with` to set `self` as the pointer value in `new`. This is okay
80            // because `new` should already be an unsized version, we merely make use of its
81            // builtin provenance copy operation.
82            unsafe { &mut *super::unsize_with(self, |_| new) }
83        }
84    }
85
86    unsafe impl<Ptr, U : ?Sized, T> CoerciblePtr<U> for core::pin::Pin<Ptr>
87    where
88        Ptr: CoerciblePtr<U, Pointee=T> + core::ops::DerefMut<Target=T>,
89        Ptr::Output: core::ops::DerefMut<Target=U>,
90    {
91        type Pointee = T;
92        type Output = core::pin::Pin<Ptr::Output>;
93        fn as_sized_ptr(&mut self) -> *mut Self::Pointee {
94            unsafe { self.as_mut().get_unchecked_mut() }
95        }
96        unsafe fn replace_ptr(self, new: *mut U) -> Self::Output {
97            let inner = core::pin::Pin::into_inner_unchecked(self);
98            let new = inner.replace_ptr(new);
99            core::pin::Pin::new_unchecked(new)
100        }
101    }
102
103    unsafe impl<T, U: ?Sized> CoerciblePtr<U> for core::ptr::NonNull<T> {
104        type Pointee = T;
105        type Output = NonNull<U>;
106        fn as_sized_ptr(&mut self) -> *mut T {
107            self.as_ptr()
108        }
109        unsafe fn replace_ptr(self, new: *mut U) -> NonNull<U> {
110            // Safety:
111            NonNull::new_unchecked(new)
112        }
113    }
114}
115
116/// Enables the unsizing of a sized pointer.
117#[repr(C)]
118pub struct Coercion<T, U : ?Sized, F : FnOnce(*const T) -> *const U = fn(*const T) -> *const U> {
119    pub(crate) coerce: F,
120    pub(crate) _phantom: PhantomData<fn(*const T) -> *const U>,
121}
122
123/// Common trait impls for `Coercion`.
124mod coercion_impls;
125
126impl<F, T, U: ?Sized> Coercion<T, U, F>
127where
128    F : FnOnce(*const T) -> *const U,
129{
130    /// Construct a new coercer.
131    ///
132    /// # Safety
133    ///
134    /// The method must not perform any action other than unsizing the pointer.
135    ///
136    /// # Usage
137    ///
138    /// ```
139    /// use unsize::Coercion;
140    /// use core::fmt::Debug;
141    ///
142    /// let c: Coercion<u32, dyn Debug> = unsafe {
143    ///     Coercion::new(|x| x)
144    /// };
145    /// ```
146    pub unsafe fn new(coerce: F) -> Self {
147        Coercion { coerce, _phantom: PhantomData }
148    }
149}
150
151macro_rules! coerce_to_dyn_trait {
152    (
153        $(for <$($generics:ident),* $(,)?>)?
154        $(#[$attr:meta])* fn $name:ident() -> $trait_type:path
155    ) => {
156        impl<'lt, T: $trait_type + 'lt, $($($generics),*)?>
157            Coercion<T, dyn $trait_type + 'lt>
158        {
159            $(#[$attr])*
160            pub fn $name() -> Self {
161                fn coerce_to_that_type<'lt, T: $trait_type + 'lt, $($($generics),*)?>(
162                    ptr: *const T
163                ) -> *const (dyn $trait_type + 'lt) {
164                    ptr
165                }
166
167                unsafe { Coercion::new(coerce_to_that_type) }
168            }
169        }
170    };
171
172    /* TODO: figure out how to make this work.
173     * Then add Iterator<Item=U>, PartialEq<Rhs>, PartialOrd<Rhs>, etc.
174    ($(#[$attr:meta])* fn $name:ident<$($param:ident),*>() -> $trait_type:ty) => {
175        impl<'lt, $($param),*, T: $trait_type + 'lt> Coercion<T, dyn ($trait_type + 'lt)> {
176            $(#[$attr])*
177            pub fn $name() -> Self {
178                fn coerce_to_that_type<'lt, $($param),*, T: $trait_type + 'lt>(
179                    ptr: *const T
180                ) -> *const (dyn $trait_type + 'lt) {
181                    ptr
182                }
183
184                Coercion { coerce: coerce_to_that_type }
185            }
186        }
187    };
188    */
189}
190
191coerce_to_dyn_trait!(
192    /// Create a coercer that unsizes and keeps dynamic type information.
193    ///
194    /// # Usage
195    ///
196    /// ```
197    /// use unsize::{Coercion, CoerceUnsize};
198    /// use core::any::Any;
199    ///
200    /// fn generic<T: Any>(ptr: &T) -> &dyn Any {
201    ///     ptr.unsize(Coercion::to_any())
202    /// }
203    /// ```
204    fn to_any() -> core::any::Any
205);
206
207coerce_to_dyn_trait!(
208    /// Create a coercer that unsizes a parameter to dynamically debug its fields.
209    ///
210    /// # Usage
211    ///
212    /// ```
213    /// use unsize::{Coercion, CoerceUnsize};
214    /// use core::fmt::Debug;
215    ///
216    /// fn generic<T: Debug>(ptr: &T) -> &dyn Debug {
217    ///     ptr.unsize(Coercion::to_debug())
218    /// }
219    /// ```
220    fn to_debug() -> core::fmt::Debug
221);
222
223coerce_to_dyn_trait!(
224    /// Create a coercer that unsizes a parameter to display it.
225    ///
226    /// # Usage
227    ///
228    /// ```
229    /// use unsize::{Coercion, CoerceUnsize};
230    /// use core::fmt::Display;
231    ///
232    /// fn generic<T: Display>(ptr: &T) -> &dyn Display {
233    ///     ptr.unsize(Coercion::to_display())
234    /// }
235    /// ```
236    fn to_display() -> core::fmt::Display
237);
238
239#[cfg(rustc_1_51)]
240impl<T, const N: usize> Coercion<[T; N], [T]> {
241    /// Create a coercer that unsizes an array to a slice.
242    ///
243    /// # Usage
244    ///
245    /// ```
246    /// use unsize::{Coercion, CoerceUnsize};
247    /// use core::fmt::Display;
248    ///
249    /// fn generic<T>(ptr: &[T; 2]) -> &[T] {
250    ///     ptr.unsize(Coercion::to_slice())
251    /// }
252    /// ```
253    pub fn to_slice() -> Self {
254        fn coerce<T, const N: usize>(
255            ptr: *const [T; N]
256        ) -> *const [T] { ptr }
257        unsafe { Coercion::new(coerce) }
258    }
259}
260
261macro_rules! coerce_to_dyn_fn {
262    (
263        $(#![$attr:meta])?
264        $($arg:ident),*
265    ) => {
266        coerce_to_dyn_fn!(
267            $(#![$attr])?
268            @<$($arg,)*>:
269            (dyn Fn($($arg,)*) -> T + 'lt),
270            (dyn FnMut($($arg,)*) -> T + 'lt),
271            (dyn FnOnce($($arg,)*) -> T + 'lt)
272        );
273    };
274    (
275        $(#![$attr:meta])?
276        @<$($arg:ident,)*>: $dyn:ty, $dyn_mut:ty, $dyn_once:ty
277    ) => {
278        coerce_to_dyn_trait! { for<Ret, $($arg),*>
279            $(#[$attr])?
280            /// Create a coercer that unsizes to a dynamically dispatched function.
281            ///
282            /// This is implemented for function arities up to the shown one
283            /// (other methods / impls are hidden in the docs for readability)
284            fn to_fn() -> Fn($($arg),*) -> Ret
285        }
286        coerce_to_dyn_trait! { for<Ret, $($arg),*>
287            $(#[$attr])?
288            /// Create a coercer that unsizes to a dynamically dispatched mutable function.
289            ///
290            /// This is implemented for function arities up to the shown one
291            /// (other methods / impls are hidden in the docs for readability)
292            fn to_fn_mut() -> FnMut($($arg),*) -> Ret
293        }
294        coerce_to_dyn_trait! { for<Ret, $($arg),*>
295            $(#[$attr])?
296            /// Create a coercer that unsizes to a dynamically dispatched once function.
297            ///
298            /// This is implemented for function arities up to the shown one
299            /// (other methods / impls are hidden in the docs for readability)
300            fn to_fn_once() -> FnOnce($($arg),*) -> Ret
301        }
302    };
303}
304
305coerce_to_dyn_fn!(#![doc(hidden)] );
306coerce_to_dyn_fn!(#![doc(hidden)] A);
307coerce_to_dyn_fn!(#![doc(hidden)] A,B);
308coerce_to_dyn_fn!(#![doc(hidden)] A,B,C);
309coerce_to_dyn_fn!(#![doc(hidden)] A,B,C,D);
310coerce_to_dyn_fn!(#![doc(hidden)] A,B,C,D,E);
311coerce_to_dyn_fn!(A,B,C,D,E,G);
312
313coerce_to_dyn_trait! {
314    for<I,>
315    /// Create a coercer that unsizes to a dynamically dispatched iterator.
316    ///
317    /// This is implemented for all iterator types. It can type-erase the concrete type to wrap an
318    /// otherwise unnameable adapter in a custom smart pointer, to store it within a struct.
319    ///
320    /// # Usage
321    ///
322    /// ```
323    /// # use unsize::CoerciblePtr;
324    /// // A non-coercible box, for demonstration purposes
325    /// struct MyBox<T: ?Sized>(Box<T>);
326    ///
327    /// unsafe impl<'lt, T, U: ?Sized + 'lt> CoerciblePtr<U> for MyBox<T> {
328    ///     // …
329    /// #   type Pointee = T;
330    /// #   type Output = MyBox<U>;
331    /// #   fn as_sized_ptr(self: &mut MyBox<T>) -> *mut T {
332    /// #       (&mut *self.0) as *mut T
333    /// #   }
334    /// #   unsafe fn replace_ptr(self, new: *mut U) -> MyBox<U> {
335    /// #       let raw: *mut T = Box::into_raw(self.0);
336    /// #       let raw: *mut U = raw.replace_ptr(new);
337    /// #       MyBox(Box::from_raw(raw))
338    /// #   }
339    /// }
340    /// # impl<T> MyBox<T> {
341    /// #    pub fn new(val: T) -> Self { MyBox(std::boxed::Box::new(val)) }
342    /// # }
343    ///
344    /// use unsize::{Coercion, CoerceUnsize};
345    /// use core::fmt::Display;
346    ///
347    /// fn maybe_empty<T: Clone>(item: &T) -> MyBox<dyn Iterator<Item=T> + '_> {
348    ///     if core::mem::size_of::<T>() % 2 == 0 {
349    ///         MyBox::new(core::iter::empty())
350    ///             .unsize(Coercion::to_iterator())
351    ///     } else {
352    ///         MyBox::new(core::iter::repeat(item.clone()))
353    ///             .unsize(Coercion::to_iterator())
354    ///     }
355    /// }
356    /// ```
357    fn to_iterator() -> Iterator<Item=I>
358}
359
360coerce_to_dyn_trait! {
361    for<I,>
362    /// Create a coercer that unsizes to a dynamically dispatched future.
363    ///
364    /// This is implemented for all iterator types. It can type-erase the concrete type to wrap an
365    /// otherwise unnameable adapter in a custom smart pointer, to store it within a struct.
366    ///
367    /// # Usage
368    ///
369    /// ```
370    /// # use core::future::Future;
371    /// # use unsize::CoerciblePtr;
372    /// // A non-coercible box, for demonstration purposes
373    /// struct MyBox<T: ?Sized>(Box<T>);
374    ///
375    /// unsafe impl<'lt, T, U: ?Sized + 'lt> CoerciblePtr<U> for MyBox<T> {
376    ///     // …
377    /// #   type Pointee = T;
378    /// #   type Output = MyBox<U>;
379    /// #   fn as_sized_ptr(self: &mut MyBox<T>) -> *mut T {
380    /// #       (&mut *self.0) as *mut T
381    /// #   }
382    /// #   unsafe fn replace_ptr(self, new: *mut U) -> MyBox<U> {
383    /// #       let raw: *mut T = Box::into_raw(self.0);
384    /// #       let raw: *mut U = raw.replace_ptr(new);
385    /// #       MyBox(Box::from_raw(raw))
386    /// #   }
387    /// }
388    /// # impl<T> MyBox<T> {
389    /// #    pub fn new(val: T) -> Self { MyBox(std::boxed::Box::new(val)) }
390    /// # }
391    ///
392    /// use unsize::{Coercion, CoerceUnsize};
393    /// use core::fmt::Display;
394    ///
395    /// fn maybe_empty<T: 'static>(val: T) -> MyBox<dyn Future<Output=T>> {
396    ///     if core::mem::size_of::<T>() % 2 == 0 {
397    ///         MyBox::new(core::future::pending())
398    ///             .unsize(Coercion::to_future())
399    ///     } else {
400    ///         MyBox::new(core::future::ready(val))
401    ///             .unsize(Coercion::to_future())
402    ///     }
403    /// }
404    /// ```
405    fn to_future() -> Future<Output=I>
406}
407
408/// ```
409/// use unsize::{Coercion, CoerceUnsize};
410/// fn arg0<F: 'static + FnOnce()>(fptr: &F) -> &dyn FnOnce() {
411///     fptr.unsize(Coercion::<_, dyn FnOnce()>::to_fn_once())
412/// }
413/// fn arg1<F: 'static + FnOnce(u32)>(fptr: &F) -> &dyn FnOnce(u32) {
414///     fptr.unsize(Coercion::<_, dyn FnOnce(u32)>::to_fn_once())
415/// }
416/// fn arg6<F: 'static + FnOnce(u32,u32,u32,u32,u32,u32)>(fptr: &F)
417///     -> &dyn FnOnce(u32,u32,u32,u32,u32,u32)
418/// {
419///     fptr.unsize(Coercion::<_, dyn FnOnce(u32,u32,u32,u32,u32,u32)>::to_fn_once())
420/// }
421/// arg0(&|| {});
422/// arg1(&|_| {});
423/// arg6(&|_,_,_,_,_,_| {});
424/// ```
425extern {}
426
427/// Add unsizing methods to pointer-like types.
428///
429/// # Safety
430/// A correct implementation must uphold, when calling `replace_ptr` with valid arguments, that the
431/// pointer target and the provenance of the pointer stay unchanged. This allows calling the
432/// coercion of inner fields of wrappers even when an invariant depends on the pointer target.
433pub unsafe trait CoerciblePtr<U: ?Sized>: Sized {
434    /// The type we point to.
435    /// This influences which kinds of unsizing are possible.
436    type Pointee;
437    /// The output type when unsizing the pointee to `U`.
438    type Output;
439    /// Get the raw inner pointer.
440    fn as_sized_ptr(&mut self) -> *mut Self::Pointee;
441    /// Replace the container inner pointer with an unsized version.
442    /// # Safety
443    /// The caller guarantees that the replacement is the same pointer, just a fat pointer variant
444    /// with a correct tag.
445    unsafe fn replace_ptr(self, _: *mut U) -> Self::Output;
446}
447
448/// An extension trait using `CoerciblePtr` for a safe interface.
449pub trait CoerceUnsize<U: ?Sized>: CoerciblePtr<U> {
450    /// Convert a pointer, as if with unsize coercion.
451    ///
452    /// See [`CoerciblePtr::unsize_with`][unsize_with] for details.
453    ///
454    /// [unsize_with]: struct.CoerciblePtr.html#method.unsize_with
455    fn unsize<F>(mut self, with: Coercion<Self::Pointee, U, F>) -> Self::Output
456    where
457        F : FnOnce(*const Self::Pointee) -> *const U,
458    {
459        unsafe {
460            let ptr = self.as_sized_ptr();
461            let new_ptr = unsize_with(ptr, with.coerce);
462            self.replace_ptr(new_ptr)
463        }
464    }
465}
466
467impl<T, U: ?Sized> CoerceUnsize<U> for T
468where
469    T: CoerciblePtr<U>
470{}
471
472/// Convert a pointer, as if with unsize coercion.
473///
474/// The result pointer will have the same provenance information as the argument `ptr`.
475///
476/// # Safety
477///
478/// The caller must guarantee that it is sound to dereference the argument and convert it into a
479/// reference. This also, very slightly, relies on some of Rust's internal layout but it will
480/// assert that they actually hold. If this sounds too risky, do not use this method. The caller
481/// must also guarantee that `with` will only execute a coercion and _not_ change the pointer
482/// itself.
483unsafe fn unsize_with<T, U: ?Sized>(
484    ptr: *mut T,
485    with: impl FnOnce(*const T) -> *const U,
486) -> *mut U {
487    let mut raw_unsized = with(ptr) as *mut U;
488
489    // Not a debug assert since it hopefully monomorphizes to a no-op (or an
490    // unconditional panic should multi-trait objects end up happening).
491    assert_eq!(Layout::for_value(&raw_unsized), Layout::new::<[usize; 2]>(),
492        "Unexpected layout of unsized pointer.");
493    debug_assert_eq!(raw_unsized as *const u8 as usize, ptr as usize,
494        "Unsize coercion seemingly changed the pointer base");
495
496    let ptr_slot = &mut raw_unsized as *mut *mut U as *mut *const u8;
497    // Safety: Not totally clear as it relies on the standard library implementation of pointers.
498    // The layout is usually valid for such a write (we've asserted that above) and all pointers
499    // are larger and at least as aligned as a single pointer to bytes.
500    // It could be that this invalidates the representation of `raw_unsized` but all currently
501    // pointers store their tag _behind_ the base pointer.
502    //
503    // There is an `unsafe`, unstable method (#75091) with the same effect and implementation.
504    //
505    // According to the `ptr::set_ptr_value` method we change provenance back to the `raw` pointer.
506    // This can be used since we haven't used any pointer from which it was derived. This
507    // invalidates the access tags of `temp_reference` and the original `raw_unsized` value but
508    // both will no longer be used.
509    unsafe { ptr_slot.write(ptr as *const u8) };
510
511    // Safety: the base pointer that we've just written was not null.
512    raw_unsized
513}
514
515/// Ensure that using `CoerceUnsize` does not import as_sized_ptr.
516///
517/// ```compile_fail
518/// use unsize::CoerceUnsize;
519/// use core::ptr::NonNull;
520///
521/// let ptr = NonNull::from(&2u32);
522/// let _ = ptr.as_sized_ptr();
523/// ```
524extern {}
525
526#[cfg(test)]
527mod tests;
528
529/// Non-`unsafe` [`struct@Coercion`] constructor for arbitrary trait bounds.
530///
531/// # Example
532// (and test!)
533///
534/// ```rust
535/// use unsize::{Coercion, CoerceUnsize};
536///
537/// trait MyFancyTrait { /* … */ }
538///
539/// fn generic<T: MyFancyTrait>(ptr: &T) -> &dyn MyFancyTrait {
540///     ptr.unsize(Coercion!(to dyn MyFancyTrait))
541/// }
542/// ```
543#[macro_export]
544macro_rules! Coercion {
545    (to dyn $($bounds:tt)*) => (
546        #[allow(unused_unsafe)] unsafe {
547            $crate::Coercion::new({
548                #[allow(unused_parens)]
549                fn coerce<'lt> (p: *const (impl $($bounds)* + 'lt)) -> *const (dyn $($bounds)* + 'lt) {
550                    p
551                }
552                coerce
553            })
554        }
555    );
556}