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}