aiscript_arena/
unsize.rs

1use core::marker::PhantomData;
2use core::ptr::NonNull;
3
4use crate::{
5    types::GcBoxInner,
6    {Gc, GcWeak},
7};
8
9/// Unsizes a [`Gc`] or [`GcWeak`] pointer.
10///
11/// This macro is a `aiscript_arena`-specific replacement for the nightly-only `CoerceUnsized` trait.
12///
13/// ## Usage
14///
15/// ```rust
16/// # use std::fmt::Display;
17/// # use aiscript_arena::{Gc, unsize};
18/// # fn main() {
19/// # aiscript_arena::arena::rootless_mutate(|mc| {
20/// // Unsizing arrays to slices.
21/// let mut slice;
22/// slice = unsize!(Gc::new(mc, [1, 2]) => [u8]);
23/// assert_eq!(slice.len(), 2);
24/// slice = unsize!(Gc::new(mc, [42; 4]) => [u8]);
25/// assert_eq!(slice.len(), 4);
26///
27/// // Unsizing values to trait objects.
28/// let mut display;
29/// display = unsize!(Gc::new(mc, "Hello world!".to_owned()) => dyn Display);
30/// assert_eq!(display.to_string(), "Hello world!");
31/// display = unsize!(Gc::new(mc, 123456) => dyn Display);
32/// assert_eq!(display.to_string(), "123456");
33/// # })
34/// # }
35/// ```
36///
37/// The `unsize` macro is safe, and will fail to compile when trying to coerce between
38/// incompatible types.
39/// ```rust,compile_fail
40/// # use std::error::Error;
41/// # use aiscript_arena::{Gc, unsize};
42/// # fn main() {
43/// # aiscript_arena::arena::rootless_mutate(|mc| {
44/// // Error: `Option<char>` doesn't implement `Error`.
45/// let _ = unsize!(Gc::new(mc, Some('💥')) => dyn Error);
46/// # })
47/// # }
48/// ```
49#[macro_export]
50macro_rules! unsize {
51    ($gc:expr => $ty:ty) => {{
52        let gc = $gc;
53        // SAFETY: the closure has a trivial body and must be a valid pointer
54        // coercion, if it compiles. Additionally, the `__CoercePtrInternal` trait
55        // ensures that the resulting GC pointer has the correct `'gc` lifetime.
56        unsafe {
57            $crate::__CoercePtrInternal::__coerce_unchecked(gc, |p: *mut _| -> *mut $ty { p })
58        }
59    }};
60}
61
62// Not public API; implementation detail of the `unsize` macro.
63//
64// Maps a raw pointer coercion (`*mut FromPtr -> *mut ToPtr`) to
65// a smart pointer coercion (`Self -> Dst`).
66#[doc(hidden)]
67pub unsafe trait __CoercePtrInternal<Dst> {
68    type FromPtr;
69    type ToPtr: ?Sized;
70    // SAFETY: `coerce` must be a valid pointer coercion; in particular, the coerced
71    // pointer must have the same address and provenance as the original.
72    unsafe fn __coerce_unchecked<F>(self, coerce: F) -> Dst
73    where
74        F: FnOnce(*mut Self::FromPtr) -> *mut Self::ToPtr;
75}
76
77unsafe impl<'gc, T, U: ?Sized> __CoercePtrInternal<Gc<'gc, U>> for Gc<'gc, T> {
78    type FromPtr = T;
79    type ToPtr = U;
80
81    #[inline(always)]
82    unsafe fn __coerce_unchecked<F>(self, coerce: F) -> Gc<'gc, U>
83    where
84        F: FnOnce(*mut T) -> *mut U,
85    {
86        unsafe {
87            let ptr = self.ptr.as_ptr() as *mut T;
88            let ptr = NonNull::new_unchecked(coerce(ptr) as *mut GcBoxInner<U>);
89            Gc {
90                ptr,
91                _invariant: PhantomData,
92            }
93        }
94    }
95}
96
97unsafe impl<'gc, T, U: ?Sized> __CoercePtrInternal<GcWeak<'gc, U>> for GcWeak<'gc, T> {
98    type FromPtr = T;
99    type ToPtr = U;
100
101    #[inline(always)]
102    unsafe fn __coerce_unchecked<F>(self, coerce: F) -> GcWeak<'gc, U>
103    where
104        F: FnOnce(*mut T) -> *mut U,
105    {
106        unsafe {
107            let inner = self.inner.__coerce_unchecked(coerce);
108            GcWeak { inner }
109        }
110    }
111}