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

@ -27,7 +27,7 @@ document-features = ["dep:document-features"]
std = ["serde_json", "serde_json/std", "hostname", "nix", "serde/std", "uuid", "backtrace", "uds", "serial_test", "alloc"]
## Enables all features that allocate in `no_std`
alloc = ["serde/alloc", "hashbrown", "postcard", "erased-serde/alloc", "ahash"]
alloc = ["serde/alloc", "hashbrown", "postcard", "erased-serde/alloc", "ahash"]
## Provide the `#[derive(SerdeAny)]` macro.
derive = ["libafl_derive"]
@ -95,25 +95,24 @@ rustversion = "1.0"
libafl_derive = { version = "0.12.0", optional = true, path = "../libafl_derive" }
static_assertions = "1.1.0"
rustversion = "1.0"
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
serde = { version = "1.0", default-features = false, features = ["derive"] } # serialization lib
erased-serde = { version = "0.3.21", default-features = false, optional = true } # erased serde
postcard = { version = "1.0", features = ["alloc"], default-features = false, optional = true } # no_std compatible serde serialization format
num_enum = { version = "0.7", default-features = false }
ahash = { version = "0.8", default-features=false, optional = true } # The hash function already used in hashbrown
backtrace = {version = "0.3", optional = true} # Used to get the stacktrace in StacktraceObserver
ahash = { version = "0.8", default-features = false, optional = true } # The hash function already used in hashbrown
backtrace = { version = "0.3", optional = true } # Used to get the stacktrace in StacktraceObserver
ctor = { optional = true, version = "0.2" }
serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] }
miniz_oxide = { version = "0.7.1", optional = true}
miniz_oxide = { version = "0.7.1", optional = true }
hostname = { version = "^0.3", optional = true } # Is there really no gethostname in the stdlib?
rand_core = { version = "0.6", optional = true }
nix = { version = "0.27", default-features = false, optional = true, features = ["signal", "socket", "poll"] }
uuid = { version = "1.4", optional = true, features = ["serde", "v4"] }
clap = {version = "4.5", features = ["derive", "wrap_help"], optional = true} # CLI parsing, for libafl_bolts::cli / the `cli` feature
clap = { version = "4.5", features = ["derive", "wrap_help"], optional = true } # CLI parsing, for libafl_bolts::cli / the `cli` feature
log = "0.4.20"
pyo3 = { version = "0.18", optional = true, features = ["serde", "macros"] }

View File

@ -6,6 +6,7 @@ use alloc::{borrow::Cow, vec::Vec};
use core::ops::{Deref, DerefMut};
use core::{
any::{type_name, TypeId},
cell::Cell,
fmt::{Debug, Formatter},
marker::PhantomData,
mem::transmute,
@ -23,40 +24,37 @@ use crate::HasLen;
#[cfg(feature = "alloc")]
use crate::Named;
/// Returns if the type `T` is equal to `U`
/// From <https://stackoverflow.com/a/60138532/7658998>
#[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]
/// Returns if the type `T` is equal to `U`, ignoring lifetimes.
#[inline] // this entire call gets optimized away :)
#[must_use]
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
@ -453,10 +451,6 @@ where
}
/// 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")]
pub trait MatchName {
/// Match for a name and return the borrowed value