Better type_eq (#2946)

* typeid that doesn't suck

* actually, that's not const!

* format, move phantomdata to alloc feature block

* a

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
This commit is contained in:
Addison Crump 2025-02-08 14:12:12 +01:00 committed by GitHub
parent abe955137f
commit 83d88546d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 5 additions and 40 deletions

View File

@ -120,6 +120,7 @@ strum = "0.26.3"
strum_macros = "0.26.4" strum_macros = "0.26.4"
toml = "0.8.19" # For parsing the injections toml file toml = "0.8.19" # For parsing the injections toml file
typed-builder = "0.20.0" # Implement the builder pattern at compiletime typed-builder = "0.20.0" # Implement the builder pattern at compiletime
typeid = "1.0.0" # Safe type_eq that doesn't rely on std specialization
uuid = { version = "1.10.0", features = ["serde", "v4"] } uuid = { version = "1.10.0", features = ["serde", "v4"] }
which = "6.0.3" which = "6.0.3"
windows = "0.59.0" windows = "0.59.0"

View File

@ -122,6 +122,7 @@ rustversion = { workspace = true }
[dependencies] [dependencies]
libafl_derive = { workspace = true, default-features = true, optional = true } libafl_derive = { workspace = true, default-features = true, optional = true }
static_assertions = { workspace = true } static_assertions = { workspace = true }
typeid = { workspace = true }
tuple_list = { version = "0.1.3" } tuple_list = { version = "0.1.3" }
hashbrown = { workspace = true, features = [ hashbrown = { workspace = true, features = [

View File

@ -6,9 +6,10 @@ use alloc::{borrow::Cow, vec::Vec};
use core::{ use core::{
any::type_name, any::type_name,
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
marker::PhantomData,
ops::{Deref, DerefMut, Index, IndexMut}, ops::{Deref, DerefMut, Index, IndexMut},
}; };
use core::{any::TypeId, cell::Cell, marker::PhantomData, mem::transmute}; use core::{any::TypeId, mem::transmute};
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -21,36 +22,9 @@ use crate::HasLen;
use crate::Named; use crate::Named;
/// Returns if the type `T` is equal to `U`, ignoring lifetimes. /// Returns if the type `T` is equal to `U`, ignoring lifetimes.
#[inline] // this entire call gets optimized away :)
#[must_use] #[must_use]
pub fn type_eq<T: ?Sized, U: ?Sized>() -> bool { pub fn type_eq<T: ?Sized, U: ?Sized>() -> bool {
// decider struct: hold a cell (which we will update if the types are unequal) and some typeid::of::<T>() == typeid::of::<U>()
// phantom data using a function pointer to allow for Copy to be implemented
struct W<'a, T: ?Sized, U: ?Sized>(&'a Cell<bool>, PhantomData<fn() -> (&'a T, &'a U)>);
// default implementation: if the types are unequal, we will use the clone implementation
impl<T: ?Sized, U: ?Sized> Clone for W<'_, T, U> {
#[inline]
fn clone(&self) -> Self {
// indicate that the types are unequal
// unfortunately, use of interior mutability (Cell) makes this not const-compatible
// not really possible to get around at this time
self.0.set(false);
W(self.0, self.1)
}
}
// specialized implementation: Copy is only implemented if the types are the same
#[expect(clippy::mismatching_type_param_order)]
impl<T: ?Sized> Copy for W<'_, T, T> {}
let detected = Cell::new(true);
// [].clone() is *specialized* in core.
// Types which implement copy will have their copy implementations used, falling back to clone.
// If the types are the same, then our clone implementation (which sets our Cell to false)
// will never be called, meaning that our Cell's content remains true.
let res = [W::<T, U>(&detected, PhantomData)].clone();
res[0].0.get()
} }
/// Borrow each member of the tuple /// Borrow each member of the tuple
@ -955,8 +929,6 @@ mod test {
} }
#[test] #[test]
#[cfg(feature = "alloc")]
#[expect(unused_qualifications)] // for type name tests
fn test_type_eq() { fn test_type_eq() {
// An alias for equality testing // An alias for equality testing
type OwnedMutSliceAlias<'a> = OwnedMutSlice<'a, u8>; type OwnedMutSliceAlias<'a> = OwnedMutSlice<'a, u8>;
@ -976,15 +948,6 @@ mod test {
// test weirder lifetime things // test weirder lifetime things
assert!(type_eq::<OwnedMutSlice<u8>, OwnedMutSlice<u8>>()); assert!(type_eq::<OwnedMutSlice<u8>, OwnedMutSlice<u8>>());
assert!(!type_eq::<OwnedMutSlice<u8>, OwnedMutSlice<u32>>()); assert!(!type_eq::<OwnedMutSlice<u8>, OwnedMutSlice<u32>>());
assert!(type_eq::<
OwnedMutSlice<u8>,
crate::ownedref::OwnedMutSlice<u8>,
>());
assert!(!type_eq::<
OwnedMutSlice<u8>,
crate::ownedref::OwnedMutSlice<u32>,
>());
} }
#[test] #[test]