Skip to content

Commit

Permalink
More robust type_eq_non_static implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Sep 19, 2023
1 parent e1a5321 commit 95b26c6
Showing 1 changed file with 24 additions and 22 deletions.
46 changes: 24 additions & 22 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use core::{
any::{type_name, TypeId},
marker::PhantomData,
mem, ptr,
};

Expand All @@ -27,30 +28,31 @@ pub(crate) fn type_eq<T: 'static, U: 'static>() -> bool {
/// `Struct<'b>`, even if either `'a` or `'b` outlives the other.
#[inline(always)]
pub(crate) fn type_eq_non_static<T: ?Sized, U: ?Sized>() -> bool {
// Inline has a weird, but desirable result on this function. It can't be
// fully inlined everywhere since it creates a function pointer of itself.
// But in practice when used here, the act of taking the address will be
// inlined, thus avoiding a function call when comparing two types.
#[inline]
fn type_id_of<T: ?Sized>() -> usize {
type_id_of::<T> as usize
non_static_type_id::<T>() == non_static_type_id::<U>()
}

/// Produces type IDs that are compatible with `TypeId::of::<T>`, but without
/// `T: 'static` bound.
fn non_static_type_id<T: ?Sized>() -> TypeId {
trait NonStaticAny {
fn get_type_id(&self) -> TypeId
where
Self: 'static;
}

// What we're doing here is comparing two function pointers of the same
// generic function to see if they are identical. If they are not
// identical then `T` and `U` are not the same type.
//
// If they are equal, then they _might_ be the same type, unless an
// optimization step reduced two different functions to the same
// implementation due to having the same body. To avoid this we are using
// a function which references itself. This is something that LLVM cannot
// merge, since each monomorphized function has a reference to a different
// global alias.
type_id_of::<T>() == type_id_of::<U>()
// This is used as a sanity check more than anything. Our previous calls
// should not have any false positives, but if they did then the odds of
// them having the same type name as well is extremely unlikely.
&& type_name::<T>() == type_name::<U>()
impl<T: ?Sized> NonStaticAny for PhantomData<T> {
fn get_type_id(&self) -> TypeId
where
Self: 'static,
{
TypeId::of::<T>()
}
}

let phantom_data = PhantomData::<T>;
NonStaticAny::get_type_id(unsafe {
mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data)
})
}

/// Reinterprets the bits of a value of one type as another type.
Expand Down

0 comments on commit 95b26c6

Please sign in to comment.