Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More robust type_eq_non_static implementation #14

Merged
merged 1 commit into from
Sep 19, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading