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}