#![doc(html_root_url = "https://docs.rs/metatype/0.2.0")]
#![feature(arbitrary_self_types)]
#![feature(raw)]
#![feature(slice_from_raw_parts)]
#![feature(specialization)]
#![warn(
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
trivial_casts,
trivial_numeric_casts,
unused_import_braces,
unused_qualifications,
unused_results,
clippy::pedantic
)]
#![allow(
clippy::must_use_candidate,
clippy::not_unsafe_ptr_arg_deref,
clippy::use_self
)]
use std::{
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}, raw
};
pub trait Type {
const METATYPE: MetaType;
type Meta: 'static;
fn meta_type(self: *const Self) -> MetaType {
Self::METATYPE
}
fn meta(self: *const Self) -> Self::Meta;
fn data(self: *const Self) -> *const ();
fn data_mut(self: *mut Self) -> *mut ();
fn dangling(t: Self::Meta) -> NonNull<Self>;
fn fatten(thin: *mut (), t: Self::Meta) -> *mut Self;
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum MetaType {
TraitObject,
Slice,
Concrete,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct TraitObject {
pub vtable: &'static (),
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Slice {
pub len: usize,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Concrete;
impl<T: ?Sized> Type for T {
#[doc(hidden)]
default const METATYPE: MetaType = MetaType::TraitObject;
#[doc(hidden)]
default type Meta = TraitObject;
#[inline]
default fn meta(self: *const Self) -> Self::Meta {
let trait_object = unsafe { transmute_coerce::<*const Self, raw::TraitObject>(self) };
assert_eq!(self as *const (), trait_object.data);
let ret = TraitObject {
vtable: unsafe { &*trait_object.vtable },
};
type_coerce(ret)
}
#[inline]
default fn data(self: *const Self) -> *const () {
let trait_object = unsafe { transmute_coerce::<*const Self, raw::TraitObject>(self) };
assert_eq!(self as *const (), trait_object.data);
self as *const ()
}
#[inline]
default fn data_mut(self: *mut Self) -> *mut () {
let trait_object = unsafe { transmute_coerce::<*const Self, raw::TraitObject>(self) };
assert_eq!(self as *mut (), trait_object.data);
self as *mut ()
}
#[inline]
default fn dangling(t: Self::Meta) -> NonNull<Self> {
let t: TraitObject = type_coerce(t);
let fake_thin = {
#[repr(align(64))]
struct Backing(u8);
static BACKING: Backing = Backing(0);
let backing: *const _ = &BACKING;
backing as *mut ()
};
let dangling_unaligned: NonNull<Self> =
NonNull::new(Self::fatten(fake_thin, type_coerce(t))).unwrap();
let dangling_unaligned: &Self = unsafe { dangling_unaligned.as_ref() };
let align = align_of_val(dangling_unaligned);
NonNull::new(Self::fatten(align as _, type_coerce(t))).unwrap()
}
#[inline]
default fn fatten(thin: *mut (), t: Self::Meta) -> *mut Self {
let t: TraitObject = type_coerce(t);
let vtable: *const () = t.vtable;
let vtable = vtable as *mut ();
let ret = raw::TraitObject { data: thin, vtable };
unsafe { transmute_coerce::<raw::TraitObject, *mut Self>(ret) }
}
}
#[doc(hidden)]
impl<T: Sized> Type for T {
const METATYPE: MetaType = MetaType::Concrete;
type Meta = Concrete;
#[inline]
fn meta(self: *const Self) -> Self::Meta {
Concrete
}
#[inline]
fn data(self: *const Self) -> *const () {
self as *const ()
}
#[inline]
fn data_mut(self: *mut Self) -> *mut () {
self as *mut ()
}
fn dangling(_t: Self::Meta) -> NonNull<Self> {
NonNull::dangling()
}
fn fatten(thin: *mut (), _t: Self::Meta) -> *mut Self {
thin.cast()
}
}
#[doc(hidden)]
impl<T: Sized> Type for [T] {
const METATYPE: MetaType = MetaType::Slice;
type Meta = Slice;
#[inline]
fn meta(self: *const Self) -> Self::Meta {
let self_ = unsafe { &*self };
assert_eq!(
(size_of_val(self_), align_of_val(self_)),
(size_of::<T>() * self_.len(), align_of::<T>())
);
Slice { len: self_.len() }
}
#[inline]
fn data(self: *const Self) -> *const () {
self as *const ()
}
#[inline]
fn data_mut(self: *mut Self) -> *mut () {
self as *mut ()
}
fn dangling(t: Self::Meta) -> NonNull<Self> {
let slice = slice_from_raw_parts_mut(NonNull::<T>::dangling().as_ptr(), t.len);
unsafe { NonNull::new_unchecked(slice) }
}
fn fatten(thin: *mut (), t: Self::Meta) -> *mut Self {
slice_from_raw_parts_mut(thin.cast(), t.len)
}
}
#[doc(hidden)]
impl Type for str {
const METATYPE: MetaType = MetaType::Slice;
type Meta = Slice;
#[inline]
fn meta(self: *const Self) -> Self::Meta {
let self_ = unsafe { &*self };
assert_eq!((size_of_val(self_), align_of_val(self_)), (self_.len(), 1));
Slice { len: self_.len() }
}
#[inline]
fn data(self: *const Self) -> *const () {
self as *const ()
}
#[inline]
fn data_mut(self: *mut Self) -> *mut () {
self as *mut ()
}
fn dangling(t: Self::Meta) -> NonNull<Self> {
let bytes: *mut [u8] = <[u8]>::dangling(t).as_ptr();
unsafe { NonNull::new_unchecked(bytes as *mut Self) }
}
fn fatten(thin: *mut (), t: Self::Meta) -> *mut Self {
<[u8]>::fatten(thin, t) as *mut Self
}
}
unsafe fn transmute_coerce<A, B>(a: A) -> B {
assert_eq!(
(size_of::<A>(), align_of::<A>()),
(size_of::<B>(), align_of::<B>()),
"can't transmute_coerce {} to {} as sizes/alignments differ",
type_name::<A>(),
type_name::<B>()
);
let b = transmute_copy(&a);
forget(a);
b
}
pub fn type_coerce<A, B>(a: A) -> B {
try_type_coerce(a)
.unwrap_or_else(|| panic!("can't coerce {} to {}", type_name::<A>(), type_name::<B>()))
}
pub fn try_type_coerce<A, B>(a: A) -> Option<B> {
trait Eq<B> {
fn eq(self) -> Option<B>;
}
struct Foo<A, B>(A, PhantomData<fn(B)>);
impl<A, B> Eq<B> for Foo<A, B> {
default fn eq(self) -> Option<B> {
None
}
}
impl<A> Eq<A> for Foo<A, A> {
fn eq(self) -> Option<A> {
Some(self.0)
}
}
Foo::<A, B>(a, PhantomData).eq()
}
pub fn type_id<T: ?Sized + 'static>() -> u64 {
let type_id = TypeId::of::<T>();
let mut hasher = std::collections::hash_map::DefaultHasher::new();
type_id.hash(&mut hasher);
hasher.finish()
}
#[cfg(test)]
mod tests {
#![allow(clippy::cast_ptr_alignment, clippy::shadow_unrelated)]
use super::{type_coerce, MetaType, Slice, TraitObject, Type};
use std::{any, ptr::NonNull};
#[test]
fn abc() {
let a: Box<usize> = Box::new(123);
assert_eq!(Type::meta_type(&*a), MetaType::Concrete);
assert_eq!(Type::meta_type(&a), MetaType::Concrete);
let a: Box<dyn any::Any> = a;
assert_eq!(Type::meta_type(&*a), MetaType::TraitObject);
assert_eq!(Type::meta_type(&a), MetaType::Concrete);
let meta: TraitObject = type_coerce(Type::meta(&*a));
let dangling = <dyn any::Any as Type>::dangling(type_coerce(meta));
let _fat = <dyn any::Any as Type>::fatten(dangling.as_ptr() as *mut (), type_coerce(meta));
let mut x: usize = 0;
let x_ptr: *mut usize = &mut x;
let mut x_ptr: NonNull<dyn any::Any> = NonNull::new(<dyn any::Any as Type>::fatten(
x_ptr as *mut (),
type_coerce(meta),
))
.unwrap();
let x_ref: &mut dyn any::Any = unsafe { x_ptr.as_mut() };
let x_ref: &mut usize = x_ref.downcast_mut().unwrap();
*x_ref = 123;
assert_eq!(x, 123);
let a: &[usize] = &[1, 2, 3];
assert_eq!(Type::meta_type(a), MetaType::Slice);
let dangling = <[String] as Type>::dangling(Slice { len: 100 });
let _fat = <[String] as Type>::fatten(dangling.as_ptr() as *mut (), Slice { len: 100 });
let a: Box<[usize]> = vec![1_usize, 2, 3].into_boxed_slice();
assert_eq!(Type::meta_type(&*a), MetaType::Slice);
assert_eq!(Type::meta_type(&a), MetaType::Concrete);
let a: &str = "abc";
assert_eq!(Type::meta_type(a), MetaType::Slice);
assert_eq!(Type::meta_type(&a), MetaType::Concrete);
let dangling = <str as Type>::dangling(Slice { len: 100 });
let _fat = <str as Type>::fatten(dangling.as_ptr() as *mut (), Slice { len: 100 });
}
}