Repository: AFLplusplus/LibAFL (language: rust)

Issue:
Specialization hack might stop working in future rust versions.
Currently, this code relies on the fact that std uses specialization for cloning arrays. This is an implementation detail, and is not supported.

https://github.com/AFLplusplus/LibAFL/blob/0573bbb159f01ecc574ae5e034cc66cd47c6abdb/libafl_bolts/src/tuples.rs#L23-L54

In a future version of rust, this specialization might be removed, breaking this code. See https://github.com/rust-lang/rust/pull/135634 and https://github.com/rust-lang/rust/issues/132442

Maybe consider using the [typeid](https://docs.rs/typeid/latest/typeid/) crate instead.


Discussion / hints:
Hey, thanks for pointing to typeid. We've needed a proper replacement for this hack for some time, I'll see if I can't migrate to this.

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 99e20896b1..57a0c02d43 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -120,6 +120,7 @@ strum = "0.26.3"
 strum_macros = "0.26.4"
 toml = "0.8.19" # For parsing the injections toml file
 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"] }
 which = "6.0.3"
 windows = "0.59.0"
diff --git a/libafl_bolts/Cargo.toml b/libafl_bolts/Cargo.toml
index 4eb69551be..f52953b1ac 100644
--- a/libafl_bolts/Cargo.toml
+++ b/libafl_bolts/Cargo.toml
@@ -122,6 +122,7 @@ rustversion = { workspace = true }
 [dependencies]
 libafl_derive = { workspace = true, default-features = true, optional = true }
 static_assertions = { workspace = true }
+typeid = { workspace = true }
 
 tuple_list = { version = "0.1.3" }
 hashbrown = { workspace = true, features = [
diff --git a/libafl_bolts/src/tuples.rs b/libafl_bolts/src/tuples.rs
index d1e779155a..a8ff63a925 100644
--- a/libafl_bolts/src/tuples.rs
+++ b/libafl_bolts/src/tuples.rs
@@ -6,9 +6,10 @@ use alloc::{borrow::Cow, vec::Vec};
 use core::{
     any::type_name,
     fmt::{Debug, Formatter},
+    marker::PhantomData,
     ops::{Deref, DerefMut, Index, IndexMut},
 };
-use core::{any::TypeId, cell::Cell, marker::PhantomData, mem::transmute};
+use core::{any::TypeId, mem::transmute};
 
 #[cfg(feature = "alloc")]
 use serde::{Deserialize, Serialize};
@@ -21,36 +22,9 @@ use crate::HasLen;
 use crate::Named;
 
 /// 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 {
-    // 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<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()
+    typeid::of::<T>() == typeid::of::<U>()
 }
 
 /// Borrow each member of the tuple
@@ -955,8 +929,6 @@ mod test {
     }
 
     #[test]
-    #[cfg(feature = "alloc")]
-    #[expect(unused_qualifications)] // for type name tests
     fn test_type_eq() {
         // An alias for equality testing
         type OwnedMutSliceAlias<'a> = OwnedMutSlice<'a, u8>;
@@ -976,15 +948,6 @@ mod test {
         // test weirder lifetime things
         assert!(type_eq::<OwnedMutSlice<u8>, OwnedMutSlice<u8>>());
         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 patch: