Stable type_eq (#2150)

* stable type eq

* whoops, wrong section

* satiate clippy

* remove extraneous comment

* explain

* bonus inline
This commit is contained in:
Addison Crump 2024-05-07 17:09:15 +02:00 committed by GitHub
parent e7e820868c
commit c1a55982b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 36 additions and 43 deletions

View File

@ -95,7 +95,6 @@ rustversion = "1.0"
libafl_derive = { version = "0.12.0", optional = true, path = "../libafl_derive" } libafl_derive = { version = "0.12.0", optional = true, path = "../libafl_derive" }
static_assertions = "1.1.0" static_assertions = "1.1.0"
rustversion = "1.0"
tuple_list = { version = "0.1.3" } tuple_list = { version = "0.1.3" }
hashbrown = { version = "0.14", features = ["serde", "ahash"], default-features = false, optional = true } # A faster hashmap, nostd compatible hashbrown = { version = "0.14", features = ["serde", "ahash"], default-features = false, optional = true } # A faster hashmap, nostd compatible
xxhash-rust = { version = "0.8.5", features = ["xxh3"], optional = true } # xxh3 hashing for rust xxhash-rust = { version = "0.8.5", features = ["xxh3"], optional = true } # xxh3 hashing for rust

View File

@ -6,6 +6,7 @@ use alloc::{borrow::Cow, vec::Vec};
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use core::{ use core::{
any::{type_name, TypeId}, any::{type_name, TypeId},
cell::Cell,
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
marker::PhantomData, marker::PhantomData,
mem::transmute, mem::transmute,
@ -23,40 +24,37 @@ use crate::HasLen;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use crate::Named; use crate::Named;
/// Returns if the type `T` is equal to `U` /// Returns if the type `T` is equal to `U`, ignoring lifetimes.
/// From <https://stackoverflow.com/a/60138532/7658998> #[inline] // this entire call gets optimized away :)
#[rustversion::nightly]
#[inline]
#[must_use]
pub const fn type_eq<T: ?Sized, U: ?Sized>() -> bool {
// Helper trait. `VALUE` is false, except for the specialization of the
// case where `T == U`.
trait TypeEq<U: ?Sized> {
const VALUE: bool;
}
// Default implementation.
impl<T: ?Sized, U: ?Sized> TypeEq<U> for T {
default const VALUE: bool = false;
}
// Specialization for `T == U`.
impl<T: ?Sized> TypeEq<T> for T {
const VALUE: bool = true;
}
<T as TypeEq<U>>::VALUE
}
/// Returns if the type `T` is equal to `U`
/// As this relies on [`type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html#note) internally,
/// there is a chance for collisions.
/// Use `nightly` if you need a perfect match at all times.
#[rustversion::not(nightly)]
#[inline]
#[must_use] #[must_use]
pub fn type_eq<T: ?Sized, U: ?Sized>() -> bool { pub fn type_eq<T: ?Sized, U: ?Sized>() -> bool {
type_name::<T>() == type_name::<U>() // decider struct: hold a cell (which we will update if the types are unequal) and some
// 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<'a, T: ?Sized, U: ?Sized> Clone for W<'a, 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
#[allow(clippy::mismatching_type_param_order)]
impl<'a, T: ?Sized> Copy for W<'a, 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
@ -453,10 +451,6 @@ where
} }
/// Match for a name and return the value /// Match for a name and return the value
///
/// # Note
/// This operation may not be 100% accurate with Rust stable, see the notes for [`type_eq`]
/// (in `nightly`, it uses [specialization](https://stackoverflow.com/a/60138532/7658998)).
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub trait MatchName { pub trait MatchName {
/// Match for a name and return the borrowed value /// Match for a name and return the borrowed value