blob: 156f9541d6890310ac7dd4d71fbc87c59191d77f [file] [log] [blame]
use alloc::boxed::Box;
use core::any::TypeId;
use core::marker::PhantomData;
use core::mem::{self, MaybeUninit};
use core::ptr;
#[cfg(feature = "unstable-debug")]
use core::any;
pub struct Any {
value: Value,
drop: unsafe fn(&mut Value),
type_id: TypeId,
/// For panic messages only. Not used for comparison.
#[cfg(feature = "unstable-debug")]
type_name: &'static str,
}
union Value {
ptr: *mut (),
inline: [MaybeUninit<usize>; 2],
}
fn is_small<T>() -> bool {
mem::size_of::<T>() <= mem::size_of::<Value>()
&& mem::align_of::<T>() <= mem::align_of::<Value>()
}
impl Any {
// This is unsafe -- caller must not hold on to the Any beyond the lifetime
// of T.
//
// Example of bad code:
//
// let s = "bad".to_owned();
// let a = Any::new(&s);
// drop(s);
//
// Now `a.as_ref()` and `a.take()` return references to a dead String.
pub(crate) unsafe fn new<T>(t: T) -> Self {
let value: Value;
let drop: unsafe fn(&mut Value);
let type_id = non_static_type_id::<T>();
if is_small::<T>() {
let mut inline = [MaybeUninit::uninit(); 2];
unsafe { ptr::write(inline.as_mut_ptr().cast::<T>(), t) };
value = Value { inline };
unsafe fn inline_drop<T>(value: &mut Value) {
unsafe { ptr::drop_in_place(value.inline.as_mut_ptr().cast::<T>()) }
}
drop = inline_drop::<T>;
} else {
let ptr = Box::into_raw(Box::new(t)).cast::<()>();
value = Value { ptr };
unsafe fn ptr_drop<T>(value: &mut Value) {
mem::drop(unsafe { Box::from_raw(value.ptr.cast::<T>()) });
}
drop = ptr_drop::<T>;
};
Any {
value,
drop,
type_id,
#[cfg(feature = "unstable-debug")]
type_name: any::type_name::<T>(),
}
}
// This is unsafe -- caller is responsible that T is the correct type.
pub(crate) unsafe fn take<T>(mut self) -> T {
if self.type_id != non_static_type_id::<T>() {
self.invalid_cast_to::<T>();
}
if is_small::<T>() {
let ptr = unsafe { self.value.inline.as_mut_ptr().cast::<T>() };
let value = unsafe { ptr::read(ptr) };
mem::forget(self);
value
} else {
let ptr = unsafe { self.value.ptr.cast::<T>() };
let box_t = unsafe { Box::from_raw(ptr) };
mem::forget(self);
*box_t
}
}
#[cfg(not(feature = "unstable-debug"))]
fn invalid_cast_to<T>(&self) -> ! {
panic!("invalid cast; enable `unstable-debug` feature to debug");
}
#[cfg(feature = "unstable-debug")]
fn invalid_cast_to<T>(&self) -> ! {
let from = self.type_name;
let to = any::type_name::<T>();
panic!("invalid cast: {} to {}", from, to);
}
}
impl Drop for Any {
fn drop(&mut self) {
unsafe { (self.drop)(&mut self.value) }
}
}
trait NonStaticAny {
fn get_type_id(&self) -> TypeId
where
Self: 'static;
}
impl<T: ?Sized> NonStaticAny for PhantomData<T> {
fn get_type_id(&self) -> TypeId
where
Self: 'static,
{
TypeId::of::<T>()
}
}
fn non_static_type_id<T: ?Sized>() -> TypeId {
let non_static_thing = PhantomData::<T>;
let thing = unsafe {
mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&non_static_thing)
};
NonStaticAny::get_type_id(thing)
}
#[test]
fn test_non_static_type_id() {
assert_eq!(non_static_type_id::<usize>(), non_static_type_id::<usize>());
assert_eq!(
non_static_type_id::<&str>(),
non_static_type_id::<&'static str>()
);
assert_ne!(non_static_type_id::<u32>(), non_static_type_id::<[u8; 4]>());
assert_ne!(
non_static_type_id::<u32>(),
non_static_type_id::<[u32; 2]>()
);
assert_ne!(non_static_type_id::<usize>(), non_static_type_id::<isize>());
assert_ne!(
non_static_type_id::<usize>(),
non_static_type_id::<&usize>()
);
assert_ne!(
non_static_type_id::<&usize>(),
non_static_type_id::<&&usize>()
);
assert_ne!(
non_static_type_id::<&usize>(),
non_static_type_id::<&mut usize>()
);
struct A;
struct B;
assert_ne!(non_static_type_id::<A>(), non_static_type_id::<B>());
}