[go: up one dir, main page]

unsize 1.0.0

A stable alternative to CoerceUnsize
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
//! This module encapsulates correct coercers and coercible pointers.
//!
//! The act of Coercion, a special kind of somewhat trivial pointer conversion, is not exposed as a
//! _safe_ trait. There are two unsafe traits: The first captures which structs unsized to which
//! dynamically sized types; which the second is applied to (wrappers around) pointers that can be
//! coerced implicitly.
//!
//! We do not have the luxury of compiler builtin checks to enforce that a particular pointer
//! conversion is sound, nor can we generate the tag of target fat pointer from thin air. Instead,
//! we use a trick. Note that both of these safety issues occur in the context of the pointer
//! conversion. Now, we can require the _user_ to _unsafely_ provide a function that implements the
//! conversion correctly. The _using_ this function is safe and enables any particular user-defined
//! pointer wrapper to safely transform itself. Note that for a limited selection of standard
//! traits we can even go so far as offer pre-built converters that are safe to use in general.
// Copyright 2019-2021 Andreas Molzer
#![no_std]
#![deny(missing_docs)]

#![allow(unused_unsafe)] // Err on the side of caution.
use core::{
    alloc::Layout,
    marker::PhantomData,
};

mod impls {
    //! Safety: Provenance is always the same as self, pointer target is simply passed through.
    use core::ptr::NonNull;
    use super::CoerciblePtr;

    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 {
            // See the mutable version.
            unsafe { &*super::unsize_with(self as *const T as *mut T, |_| new) }
        }
    }

    /// Safety: Provenance is always the same as self.
    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 {
            // (Explanation should apply to the const version too).
            // We want the `new` pointer with provenance of `self`. This is because in
            // `as_sized_ptr` we had only borrowed the mutably reference and the usage of passing
            // it as argument to this method has invalidated this borrow.
            // We reuse `unsize_with` to set `self` as the pointer value in `new`. This is okay
            // because `new` should already be an unsized version, we merely make use of its
            // builtin provenance copy operation.
            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> {
            // Safety:
            NonNull::new_unchecked(new)
        }
    }
}

/// Enables the unsizing of a sized pointer.
#[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>,
}

/// Common trait impls for `Coercion`.
mod coercion_impls;

impl<F, T, U: ?Sized> Coercion<T, U, F>
where
    F : FnOnce(*const T) -> *const U,
{
    /// Construct a new coercer.
    ///
    /// # Safety
    ///
    /// The method must not perform any action other than unsizing the pointer.
    ///
    /// # Usage
    ///
    /// ```
    /// use unsize::Coercion;
    /// use core::fmt::Debug;
    ///
    /// let c: Coercion<u32, dyn Debug> = unsafe {
    ///     Coercion::new(|x| x)
    /// };
    /// ```
    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) }
            }
        }
    };

    /* TODO: figure out how to make this work.
     * Then add Iterator<Item=U>, PartialEq<Rhs>, PartialOrd<Rhs>, etc.
    ($(#[$attr:meta])* fn $name:ident<$($param:ident),*>() -> $trait_type:ty) => {
        impl<'lt, $($param),*, T: $trait_type + 'lt> Coercion<T, dyn ($trait_type + 'lt)> {
            $(#[$attr])*
            pub fn $name() -> Self {
                fn coerce_to_that_type<'lt, $($param),*, T: $trait_type + 'lt>(
                    ptr: *const T
                ) -> *const (dyn $trait_type + 'lt) {
                    ptr
                }

                Coercion { coerce: coerce_to_that_type }
            }
        }
    };
    */
}

coerce_to_dyn_trait!(
    /// Create a coercer that unsizes and keeps dynamic type information.
    ///
    /// # Usage
    ///
    /// ```
    /// use unsize::{Coercion, CoerceUnsize};
    /// use core::any::Any;
    ///
    /// fn generic<T: Any>(ptr: &T) -> &dyn Any {
    ///     ptr.unsize(Coercion::to_any())
    /// }
    /// ```
    fn to_any() -> core::any::Any
);

coerce_to_dyn_trait!(
    /// Create a coercer that unsizes a parameter to dynamically debug its fields.
    ///
    /// # Usage
    ///
    /// ```
    /// use unsize::{Coercion, CoerceUnsize};
    /// use core::fmt::Debug;
    ///
    /// fn generic<T: Debug>(ptr: &T) -> &dyn Debug {
    ///     ptr.unsize(Coercion::to_debug())
    /// }
    /// ```
    fn to_debug() -> core::fmt::Debug
);

coerce_to_dyn_trait!(
    /// Create a coercer that unsizes a parameter to display it.
    ///
    /// # Usage
    ///
    /// ```
    /// use unsize::{Coercion, CoerceUnsize};
    /// use core::fmt::Display;
    ///
    /// fn generic<T: Display>(ptr: &T) -> &dyn Display {
    ///     ptr.unsize(Coercion::to_display())
    /// }
    /// ```
    fn to_display() -> core::fmt::Display
);

#[cfg(rustc_1_51)]
impl<T, const N: usize> Coercion<[T; N], [T]> {
    /// Create a coercer that unsizes an array to a slice.
    ///
    /// # Usage
    ///
    /// ```
    /// use unsize::{Coercion, CoerceUnsize};
    /// use core::fmt::Display;
    ///
    /// fn generic<T>(ptr: &[T; 2]) -> &[T] {
    ///     ptr.unsize(Coercion::to_slice())
    /// }
    /// ```
    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])?
            /// Create a coercer that unsizes to a dynamically dispatched function.
            ///
            /// This is implemented for function arities up to the shown one
            /// (other methods / impls are hidden in the docs for readability)
            fn to_fn() -> Fn($($arg),*) -> Ret
        }
        coerce_to_dyn_trait! { for<Ret, $($arg),*>
            $(#[$attr])?
            /// Create a coercer that unsizes to a dynamically dispatched mutable function.
            ///
            /// This is implemented for function arities up to the shown one
            /// (other methods / impls are hidden in the docs for readability)
            fn to_fn_mut() -> FnMut($($arg),*) -> Ret
        }
        coerce_to_dyn_trait! { for<Ret, $($arg),*>
            $(#[$attr])?
            /// Create a coercer that unsizes to a dynamically dispatched once function.
            ///
            /// This is implemented for function arities up to the shown one
            /// (other methods / impls are hidden in the docs for readability)
            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);

/// ```
/// use unsize::{Coercion, CoerceUnsize};
/// fn arg0<F: 'static + FnOnce()>(fptr: &F) -> &dyn FnOnce() {
///     fptr.unsize(Coercion::<_, dyn FnOnce()>::to_fn_once())
/// }
/// fn arg1<F: 'static + FnOnce(u32)>(fptr: &F) -> &dyn FnOnce(u32) {
///     fptr.unsize(Coercion::<_, dyn FnOnce(u32)>::to_fn_once())
/// }
/// fn arg6<F: 'static + FnOnce(u32,u32,u32,u32,u32,u32)>(fptr: &F)
///     -> &dyn FnOnce(u32,u32,u32,u32,u32,u32)
/// {
///     fptr.unsize(Coercion::<_, dyn FnOnce(u32,u32,u32,u32,u32,u32)>::to_fn_once())
/// }
/// arg0(&|| {});
/// arg1(&|_| {});
/// arg6(&|_,_,_,_,_,_| {});
/// ```
extern {}

/// Add unsizing methods to pointer-like types.
///
/// # Safety
/// A correct implementation must uphold, when calling `replace_ptr` with valid arguments, that the
/// pointer target and the provenance of the pointer stay unchanged. This allows calling the
/// coercion of inner fields of wrappers even when an invariant depends on the pointer target.
pub unsafe trait CoerciblePtr<U: ?Sized>: Sized {
    /// The type we point to.
    /// This influences which kinds of unsizing are possible.
    type Pointee;
    /// The output type when unsizing the pointee to `U`.
    type Output;
    /// Get the raw inner pointer.
    fn as_sized_ptr(&mut self) -> *mut Self::Pointee;
    /// Replace the container inner pointer with an unsized version.
    /// # Safety
    /// The caller guarantees that the replacement is the same pointer, just a fat pointer variant
    /// with a correct tag.
    unsafe fn replace_ptr(self, _: *mut U) -> Self::Output;
}

/// An extension trait using `CoerciblePtr` for a safe interface.
pub trait CoerceUnsize<U: ?Sized>: CoerciblePtr<U> {
    /// Convert a pointer, as if with unsize coercion.
    ///
    /// See [`CoerciblePtr::unsize_with`][unsize_with] for details.
    ///
    /// [unsize_with]: struct.CoerciblePtr.html#method.unsize_with
    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>
{}

/// Convert a pointer, as if with unsize coercion.
///
/// The result pointer will have the same provenance information as the argument `ptr`.
///
/// # Safety
///
/// The caller must guarantee that it is sound to dereference the argument and convert it into a
/// reference. This also, very slightly, relies on some of Rust's internal layout but it will
/// assert that they actually hold. If this sounds too risky, do not use this method. The caller
/// must also guarantee that `with` will only execute a coercion and _not_ change the pointer
/// itself.
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;

    // Not a debug assert since it hopefully monomorphizes to a no-op (or an
    // unconditional panic should multi-trait objects end up happening).
    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;
    // Safety: Not totally clear as it relies on the standard library implementation of pointers.
    // The layout is usually valid for such a write (we've asserted that above) and all pointers
    // are larger and at least as aligned as a single pointer to bytes.
    // It could be that this invalidates the representation of `raw_unsized` but all currently
    // pointers store their tag _behind_ the base pointer.
    //
    // There is an `unsafe`, unstable method (#75091) with the same effect and implementation.
    //
    // According to the `ptr::set_ptr_value` method we change provenance back to the `raw` pointer.
    // This can be used since we haven't used any pointer from which it was derived. This
    // invalidates the access tags of `temp_reference` and the original `raw_unsized` value but
    // both will no longer be used.
    unsafe { ptr_slot.write(ptr as *const u8) };

    // Safety: the base pointer that we've just written was not null.
    raw_unsized
}

/// Ensure that using `CoerceUnsize` does not import as_sized_ptr.
///
/// ```compile_fail
/// use unsize::CoerceUnsize;
/// use core::ptr::NonNull;
///
/// let ptr = NonNull::from(&2u32);
/// let _ = ptr.as_sized_ptr();
/// ```
extern {}

#[cfg(test)]
mod tests;

/// Non-`unsafe` [`struct@Coercion`] constructor for arbitrary trait bounds.
///
/// # Example
// (and test!)
///
/// ```rust
/// use unsize::{Coercion, CoerceUnsize};
///
/// trait MyFancyTrait { /* … */ }
///
/// fn generic<T: MyFancyTrait>(ptr: &T) -> &dyn MyFancyTrait {
///     ptr.unsize(Coercion!(to dyn MyFancyTrait))
/// }
/// ```
#[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
            })
        }
    );
}