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

Stable type_eq #2150

Merged
merged 6 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
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
13 changes: 6 additions & 7 deletions libafl_bolts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -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"
addisoncrump marked this conversation as resolved.
Show resolved Hide resolved
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"] }
Expand Down
62 changes: 28 additions & 34 deletions libafl_bolts/src/tuples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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]
/// Returns if the type `T` is equal to `U`, ignoring lifetimes.
#[inline] // this entire call gets optimized away :)
#[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;
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
// 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 {
addisoncrump marked this conversation as resolved.
Show resolved Hide resolved
// 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)
}
}

<T as TypeEq<U>>::VALUE
}
// 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> {}

/// 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]
pub fn type_eq<T: ?Sized, U: ?Sized>() -> bool {
type_name::<T>() == type_name::<U>()
let detected = Cell::new(true);
// [].clone() is *specialized* in core.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any documentation on this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like, anything official/some blog post, forum entry, docstring, ...?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add those to the code?

// 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
Expand Down Expand Up @@ -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
Expand Down
Loading