1#![doc(html_root_url = "https://docs.rs/metatype/0.2.2")]
34#![feature(arbitrary_self_types_pointers)]
35#![feature(ptr_metadata)]
36#![feature(specialization)]
37#![warn(
38 missing_copy_implementations,
39 missing_debug_implementations,
40 missing_docs,
41 trivial_casts,
42 trivial_numeric_casts,
43 unused_import_braces,
44 unused_qualifications,
45 unused_results,
46 clippy::pedantic
47)] #![allow(
49 clippy::must_use_candidate,
50 clippy::not_unsafe_ptr_arg_deref,
51 clippy::use_self,
52 clippy::missing_panics_doc,
53 incomplete_features
54)]
55
56use std::{
57 any::{type_name, TypeId}, hash::{Hash, Hasher}, marker::PhantomData, mem::{align_of, align_of_val, forget, size_of, size_of_val, transmute_copy}, ptr::{slice_from_raw_parts_mut, NonNull}
58};
59
60pub trait Type {
62 const METATYPE: MetaType;
64 type Meta: 'static;
66 fn meta_type(self: *const Self) -> MetaType {
68 Self::METATYPE
69 }
70 fn meta(self: *const Self) -> Self::Meta;
72 fn data(self: *const Self) -> *const ();
74 fn data_mut(self: *mut Self) -> *mut ();
76 fn dangling(t: Self::Meta) -> NonNull<Self>;
78 fn fatten(thin: *mut (), t: Self::Meta) -> *mut Self;
80}
81#[derive(Copy, Clone, PartialEq, Eq, Debug)]
83pub enum MetaType {
84 TraitObject,
86 Slice,
88 Concrete,
90}
91
92#[derive(Copy, Clone, PartialEq, Eq, Debug)]
94pub struct TraitObject {
95 pub vtable: &'static (),
97}
98#[derive(Copy, Clone, PartialEq, Eq, Debug)]
100pub struct Slice {
101 pub len: usize,
103}
104#[derive(Copy, Clone, PartialEq, Eq, Debug)]
106pub struct Concrete;
107
108impl<T: ?Sized> Type for T {
109 #[doc(hidden)]
110 default const METATYPE: MetaType = MetaType::TraitObject;
111 #[doc(hidden)]
112 default type Meta = TraitObject;
113 #[inline]
114 default fn meta(self: *const Self) -> Self::Meta {
115 let ret = TraitObject {
116 vtable: unsafe { transmute_coerce(std::ptr::metadata(self)) },
117 };
118 type_coerce(ret)
119 }
120 #[inline]
121 default fn data(self: *const Self) -> *const () {
122 self.cast()
123 }
124 #[inline]
125 default fn data_mut(self: *mut Self) -> *mut () {
126 self.cast()
127 }
128 #[inline]
129 default fn dangling(t: Self::Meta) -> NonNull<Self> {
130 let t: TraitObject = type_coerce(t);
131 let fake_thin = {
134 #[allow(dead_code)]
135 #[repr(align(64))]
136 struct Backing(u8);
137 static BACKING: Backing = Backing(0);
138 let backing: *const _ = &BACKING;
139 backing.cast::<()>().cast_mut()
140 };
141 let dangling_unaligned: NonNull<Self> =
142 NonNull::new(Self::fatten(fake_thin, type_coerce(t))).unwrap();
143 let dangling_unaligned: &Self = unsafe { dangling_unaligned.as_ref() };
144 let align = align_of_val(dangling_unaligned);
145 NonNull::new(Self::fatten(align as _, type_coerce(t))).unwrap()
146 }
147 #[inline]
148 default fn fatten(thin: *mut (), t: Self::Meta) -> *mut Self {
149 let t: TraitObject = type_coerce(t);
150 let vtable: *const () = t.vtable;
151 let vtable = vtable.cast_mut();
152 std::ptr::from_raw_parts_mut(thin, unsafe { transmute_coerce(vtable) })
153 }
154}
155#[doc(hidden)]
156impl<T: Sized> Type for T {
157 const METATYPE: MetaType = MetaType::Concrete;
158 type Meta = Concrete;
159 #[inline]
160 fn meta(self: *const Self) -> Self::Meta {
161 Concrete
162 }
163 #[inline]
164 fn data(self: *const Self) -> *const () {
165 self.cast()
166 }
167 #[inline]
168 fn data_mut(self: *mut Self) -> *mut () {
169 self.cast()
170 }
171 fn dangling(_t: Self::Meta) -> NonNull<Self> {
172 NonNull::dangling()
173 }
174 fn fatten(thin: *mut (), _t: Self::Meta) -> *mut Self {
175 thin.cast()
176 }
177}
178#[doc(hidden)]
179impl<T: Sized> Type for [T] {
180 const METATYPE: MetaType = MetaType::Slice;
181 type Meta = Slice;
182 #[allow(clippy::manual_slice_size_calculation)]
183 #[inline]
184 fn meta(self: *const Self) -> Self::Meta {
185 let self_ = unsafe { &*self }; assert_eq!(
187 (size_of_val(self_), align_of_val(self_)),
188 (size_of::<T>() * self_.len(), align_of::<T>())
189 );
190 Slice { len: self_.len() }
191 }
192 #[inline]
193 fn data(self: *const Self) -> *const () {
194 self.cast()
195 }
196 #[inline]
197 fn data_mut(self: *mut Self) -> *mut () {
198 self.cast()
199 }
200 fn dangling(t: Self::Meta) -> NonNull<Self> {
201 let slice = slice_from_raw_parts_mut(NonNull::<T>::dangling().as_ptr(), t.len);
202 unsafe { NonNull::new_unchecked(slice) }
203 }
204 fn fatten(thin: *mut (), t: Self::Meta) -> *mut Self {
205 slice_from_raw_parts_mut(thin.cast(), t.len)
206 }
207}
208#[doc(hidden)]
209impl Type for str {
210 const METATYPE: MetaType = MetaType::Slice;
211 type Meta = Slice;
212 #[inline]
213 fn meta(self: *const Self) -> Self::Meta {
214 let self_ = unsafe { &*self }; assert_eq!((size_of_val(self_), align_of_val(self_)), (self_.len(), 1));
216 Slice { len: self_.len() }
217 }
218 #[inline]
219 fn data(self: *const Self) -> *const () {
220 self.cast()
221 }
222 #[inline]
223 fn data_mut(self: *mut Self) -> *mut () {
224 self.cast()
225 }
226 fn dangling(t: Self::Meta) -> NonNull<Self> {
227 let bytes: *mut [u8] = <[u8]>::dangling(t).as_ptr();
228 unsafe { NonNull::new_unchecked(bytes as *mut Self) }
229 }
230 fn fatten(thin: *mut (), t: Self::Meta) -> *mut Self {
231 <[u8]>::fatten(thin, t) as *mut Self
232 }
233}
234
235unsafe fn transmute_coerce<A, B>(a: A) -> B {
236 assert_eq!(
237 (size_of::<A>(), align_of::<A>()),
238 (size_of::<B>(), align_of::<B>()),
239 "can't transmute_coerce {} to {} as sizes/alignments differ",
240 type_name::<A>(),
241 type_name::<B>()
242 );
243 let b = transmute_copy(&a);
244 forget(a);
245 b
246}
247
248pub fn type_coerce<A, B>(a: A) -> B {
254 try_type_coerce(a)
255 .unwrap_or_else(|| panic!("can't coerce {} to {}", type_name::<A>(), type_name::<B>()))
256}
257
258pub fn try_type_coerce<A, B>(a: A) -> Option<B> {
264 trait Eq<B> {
265 fn eq(self) -> Option<B>;
266 }
267
268 struct Foo<A, B>(A, PhantomData<fn(B)>);
269
270 impl<A, B> Eq<B> for Foo<A, B> {
271 default fn eq(self) -> Option<B> {
272 None
273 }
274 }
275 #[allow(clippy::mismatching_type_param_order)]
276 impl<A> Eq<A> for Foo<A, A> {
277 fn eq(self) -> Option<A> {
278 Some(self.0)
279 }
280 }
281
282 Foo::<A, B>(a, PhantomData).eq()
283}
284
285pub fn type_id<T: ?Sized + 'static>() -> u64 {
289 let type_id = TypeId::of::<T>();
290 let mut hasher = std::collections::hash_map::DefaultHasher::new();
291 type_id.hash(&mut hasher);
292 hasher.finish()
293}
294
295#[cfg(test)]
296mod tests {
297 #![allow(clippy::cast_ptr_alignment, clippy::shadow_unrelated)]
298 use super::{type_coerce, MetaType, Slice, TraitObject, Type};
299 use std::{any, ptr::NonNull};
300
301 #[test]
302 fn abc() {
303 let a: Box<usize> = Box::new(123);
304 assert_eq!(Type::meta_type(&*a), MetaType::Concrete);
305 assert_eq!(Type::meta_type(&a), MetaType::Concrete);
306 let a: Box<dyn any::Any> = a;
307 assert_eq!(Type::meta_type(&*a), MetaType::TraitObject);
308 assert_eq!(Type::meta_type(&a), MetaType::Concrete);
309 let meta: TraitObject = type_coerce(Type::meta(&*a));
310 let dangling = <dyn any::Any as Type>::dangling(type_coerce(meta));
311 let _fat = <dyn any::Any as Type>::fatten(dangling.as_ptr().cast(), type_coerce(meta));
312 let mut x: usize = 0;
313 let x_ptr: *mut usize = &mut x;
314 let mut x_ptr: NonNull<dyn any::Any> = NonNull::new(<dyn any::Any as Type>::fatten(
315 x_ptr.cast(),
316 type_coerce(meta),
317 ))
318 .unwrap();
319 let x_ref: &mut dyn any::Any = unsafe { x_ptr.as_mut() };
320 let x_ref: &mut usize = x_ref.downcast_mut().unwrap();
321 *x_ref = 123;
322 assert_eq!(x, 123);
323
324 let a: &[usize] = &[1, 2, 3];
325 assert_eq!(Type::meta_type(a), MetaType::Slice);
326 let dangling = <[String] as Type>::dangling(Slice { len: 100 });
327 let _fat = <[String] as Type>::fatten(dangling.as_ptr().cast(), Slice { len: 100 });
328
329 let a: Box<[usize]> = vec![1_usize, 2, 3].into_boxed_slice();
330 assert_eq!(Type::meta_type(&*a), MetaType::Slice);
331 assert_eq!(Type::meta_type(&a), MetaType::Concrete);
332
333 let a: &str = "abc";
334 assert_eq!(Type::meta_type(a), MetaType::Slice);
335 assert_eq!(Type::meta_type(&a), MetaType::Concrete);
336 let dangling = <str as Type>::dangling(Slice { len: 100 });
337 let _fat = <str as Type>::fatten(dangling.as_ptr().cast(), Slice { len: 100 });
338 }
339}