try_specialize/unreliable/
impls_trait.rs

1use core::fmt::{Debug, Display, Write as FmtWrite};
2use core::future::{Future, IntoFuture};
3use core::hash::Hash;
4use core::ops::Deref;
5use core::panic::{RefUnwindSafe, UnwindSafe};
6use core::str::FromStr;
7#[cfg(feature = "std")]
8use std::io::{Read as IoRead, Write as IoWrite};
9
10use crate::LifetimeFree;
11
12/// Generates a function which returns `true` if the given type implements
13/// specified trait. Note that all the lifetimes are erased and not accounted
14/// for.
15///
16/// Library tests ensure that the `impls_trait` checks are performed at compile
17/// time and are fully optimized with no runtime cost at `opt-level >= 1`. Note
18/// that the release profile uses `opt-level = 3` by default.
19///
20/// Custom attributes:
21/// - `#[auto_doc]` attribute enables automatic documentation generation for the
22///   generated function including the `Reliability` documentation section.
23/// - `#[+reliability_doc]` attribute enables automatic generation of
24///   `Reliability` documentation section for the generated function.
25///
26/// # Reliability
27///
28/// While it is unlikely, there is still a possibility that the functions
29/// generated by this macro may return false negatives in future Rust versions.
30///
31/// The correctness of the results returned by the functions depends on
32/// the following:
33/// - Documented behavior that if `T` implements `Eq`, two `Rc`s that point to
34///   the same allocation are always equal:
35///   <https://doc.rust-lang.org/1.82.0/std/rc/struct.Rc.html#method.eq>.
36/// - Undocumented behavior that the `Rc::partial_eq` implementation for `T: Eq`
37///   will not use `PartialEq::eq` if both `Rc`s point to the same memory
38///   location.
39/// - The assumption that the undocumented short-circuit behavior described
40///   above will be retained for optimization purposes.
41///
42/// There is no formal guarantee that the undocumented behavior described above
43/// will be retained. If the implementation changes in a future Rust version,
44/// the function may return a false negative, that is, it may return `false`,
45/// even though `T` implements the trait. However, the implementation guarantees
46/// that a false positive result is impossible, i.e., the function will never
47/// return true if `T` does not implement the trait in any future Rust version.
48///
49/// Details:
50/// - <https://internals.rust-lang.org/t/rc-uses-visibly-behavior-changing-specialization-is-that-okay/16173/6>,
51/// - <https://users.rust-lang.org/t/hack-to-specialize-w-write-for-vec-u8/100366>,
52/// - <https://doc.rust-lang.org/1.82.0/std/rc/struct.Rc.html#method.eq>,
53/// - <https://github.com/rust-lang/rust/issues/42655>.
54///
55/// # Examples
56///
57/// ```rust
58/// # #[cfg(all(feature = "alloc", feature = "unreliable"))] {
59/// use try_specialize::define_impls_trait_ignore_lt_fn;
60///
61/// define_impls_trait_ignore_lt_fn!(
62///     #[auto_doc] pub impls_into_iterator_u32: IntoIterator<Item = u32>
63/// );
64/// assert!(impls_into_iterator_u32::<[u32; 4]>());
65/// assert!(impls_into_iterator_u32::<Vec<u32>>());
66/// assert!(!impls_into_iterator_u32::<Vec<i32>>());
67/// # }
68/// ```
69///
70/// ```rust
71/// # #[cfg(all(feature = "alloc", feature = "unreliable"))] {
72/// use try_specialize::define_impls_trait_ignore_lt_fn;
73///
74/// define_impls_trait_ignore_lt_fn!(
75///     pub impls_copy_eq_ord: Copy + Eq + Ord
76/// );
77/// assert!(impls_copy_eq_ord::<u32>());
78/// assert!(impls_copy_eq_ord::<[u32; 4]>());
79/// assert!(!impls_copy_eq_ord::<Vec<u32>>());
80/// # }
81/// ```
82///
83/// ```rust
84/// # #[cfg(all(feature = "alloc", feature = "unreliable"))] {
85/// use try_specialize::define_impls_trait_ignore_lt_fn;
86///
87/// pub trait IsRef {}
88/// impl<T> IsRef for &T where T: ?Sized {}
89///
90/// pub trait IsMutRef {}
91/// impl<T> IsMutRef for &mut T where T: ?Sized {}
92///
93/// pub trait IsBox {}
94/// impl<T> IsBox for Box<T> where T: ?Sized {}
95///
96/// define_impls_trait_ignore_lt_fn!(
97///     #[+reliability_doc]
98///     /// Returns `true` if the given type is a const reference like `&T`.
99///     pub is_ref: IsRef
100/// );
101///
102/// define_impls_trait_ignore_lt_fn!(
103///     #[+reliability_doc]
104///     /// Returns `true` if the given type is a mutable reference like
105///     /// `&mut T`.
106///     pub is_mut_ref: IsMutRef
107/// );
108///
109/// define_impls_trait_ignore_lt_fn!(
110///     #[+reliability_doc]
111///     /// Returns `true` if the given type is a `Box<T>`-type.
112///     pub is_box: IsBox
113/// );
114///
115/// assert!(!is_ref::<u32>());
116/// assert!(!is_ref::<[u32; 4]>());
117/// assert!(!is_ref::<Vec<u32>>());
118/// assert!(is_ref::<&u32>());
119/// assert!(is_ref::<&[u32]>());
120/// assert!(!is_ref::<&mut u32>());
121///
122/// assert!(!is_mut_ref::<u32>());
123/// assert!(!is_mut_ref::<&u32>());
124/// assert!(is_mut_ref::<&mut u32>());
125///
126/// assert!(!is_box::<u32>());
127/// assert!(!is_box::<&u32>());
128/// assert!(!is_box::<&mut u32>());
129/// assert!(is_box::<Box<u32>>());
130/// assert!(is_box::<Box<(char, u32, i128)>>());
131/// # }
132/// ```
133#[macro_export]
134macro_rules! define_impls_trait_ignore_lt_fn {
135    ( #[auto_doc] $( #[$meta:meta] )* $vis:vis $fn_name:ident: $( $bounds:tt )+ ) => {
136        define_impls_trait_ignore_lt_fn! {
137            #[doc = "Returns `true` if the given type implements `"]
138            #[doc = stringify!( $( $bounds )+ )]
139            #[doc = "`."]
140            ///
141            /// Use [`define_impls_trait_ignore_lt_fn`] macro to generate other
142            /// trait implementation check functions.
143            ///
144            /// Library tests ensure that the `impls_trait` checks are performed
145            /// at compile time and fully optimized with no runtime cost at
146            /// `opt-level >= 1`. Note that the release profile uses
147            /// `opt-level = 3` by default.
148            ///
149            /// [`define_impls_trait_ignore_lt_fn`]: https://docs.rs/try-specialize/latest/try_specialize/macro.define_impls_trait_ignore_lt_fn.html
150            ///
151            #[+reliability_doc]
152            $( #[$meta] )*
153            $vis $fn_name: $( $bounds )+
154        }
155    };
156    (
157        $( #[$meta1:meta] )* #[+reliability_doc] $( #[$meta2:meta] )*
158        $vis:vis $fn_name:ident: $( $bounds:tt )+
159    ) => {
160        define_impls_trait_ignore_lt_fn! {
161            $( #[$meta1] )*
162            ///
163            /// # Reliability
164            ///
165            /// While it is unlikely, there is still a possibility that this
166            /// function may return false negatives in future Rust versions.
167            ///
168            /// The correctness of the results returned by the functions depends
169            /// on the following:
170            /// - Documented behavior that if `T` implements `Eq`, two `Rc`s
171            ///   that point to the same allocation are always equal:
172            ///   <https://doc.rust-lang.org/1.82.0/std/rc/struct.Rc.html#method.eq>.
173            /// - Undocumented behavior that the `Rc::partial_eq` implementation
174            ///   for `T: Eq` will not use `PartialEq::eq` if both `Rc`s point
175            ///   to the same memory location.
176            /// - The assumption that the undocumented short-circuit behavior
177            ///   described above will be retained for optimization purposes.
178            ///
179            /// There is no formal guarantee that the undocumented behavior
180            /// described above will be retained. If the implementation changes
181            /// in a future Rust version, the function may return a false
182            /// negative, that is, it may return `false`, even though `T`
183            /// implements the trait. However, the implementation guarantees
184            /// that a false positive result is impossible, i.e., the function
185            /// will never return true if `T` does not implement the trait in
186            /// any future Rust version.
187            ///
188            /// Details:
189            /// - <https://internals.rust-lang.org/t/rc-uses-visibly-behavior-changing-specialization-is-that-okay/16173/6>,
190            /// - <https://users.rust-lang.org/t/hack-to-specialize-w-write-for-vec-u8/100366>,
191            /// - <https://doc.rust-lang.org/1.82.0/std/rc/struct.Rc.html#method.eq>,
192            /// - <https://github.com/rust-lang/rust/issues/42655>.
193            ///
194            $( #[$meta2] )*
195            $vis $fn_name: $( $bounds )+
196        }
197    };
198    ( $( #[$meta:meta] )* $vis:vis $fn_name:ident: $( $bounds:tt )+ ) => {
199        $( #[$meta] )*
200        #[inline]
201        #[must_use]
202        $vis fn $fn_name<T>() -> bool
203        where
204            T: ?Sized
205        {
206            struct Impl<'a, T>(&'a ::core::cell::Cell<bool>, ::core::marker::PhantomData<T>)
207            where
208                T: ?Sized;
209
210            impl<T> PartialEq for Impl<'_, T>
211            where
212                T: ?Sized,
213            {
214                fn eq(&self, _other: &Self) -> bool {
215                    let _ = self.0.set(true);
216                    true
217                }
218            }
219
220            impl<T> Eq for Impl<'_, T> where T: ?Sized + $( $bounds )+ {}
221
222            let not_impls_trait = ::core::cell::Cell::new(false);
223            let rc = $crate::macro_deps::Rc::new(Impl(
224                &not_impls_trait,
225                ::core::marker::PhantomData::<T>
226            ));
227            let _ = rc == rc;
228            !not_impls_trait.get()
229        }
230    };
231}
232
233define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_sized_weak: Sized);
234define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_send_weak: Send);
235define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_sync_weak: Sync);
236define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_unpin_weak: Unpin);
237define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_unwind_safe_weak: UnwindSafe);
238define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_ref_unwind_safe_weak: RefUnwindSafe);
239
240define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_deref_weak: Deref);
241define_impls_trait_ignore_lt_fn!(
242    #[auto_doc]
243    /// # Examples
244    ///
245    /// ```rust
246    /// # #[cfg(all(feature = "alloc", feature = "unreliable"))] {
247    /// use core::sync::atomic::{AtomicU32, Ordering as AtomicOrdering};
248    ///
249    /// use try_specialize::unreliable::impls_copy_weak;
250    ///
251    /// #[derive(Eq, PartialEq, Debug)]
252    /// pub struct ArrayLike<T, const N: usize> {
253    /// #   inner: [T; N]
254    /// }
255    ///
256    /// impl<T, const N: usize> From<[T; N]> for ArrayLike<T, N> {
257    ///     #[inline]
258    ///     fn from(value: [T; N]) -> Self {
259    ///         // ...
260    ///         # Self { inner: value }
261    ///     }
262    /// }
263    ///
264    /// impl<T, const N: usize> AsRef<[T; N]> for ArrayLike<T, N> {
265    ///     #[inline]
266    ///     fn as_ref(&self) -> &[T; N] {
267    ///         // ...
268    ///         # &self.inner
269    ///     }
270    /// }
271    ///
272    /// static DEBUG: AtomicU32 = AtomicU32::new(0);
273    ///
274    /// impl<T, const N: usize> Clone for ArrayLike<T, N>
275    /// where
276    ///     T: Clone
277    /// {
278    ///     #[inline]
279    ///     fn clone(&self) -> Self {
280    ///         if impls_copy_weak::<T>() {
281    ///             DEBUG.store(101, AtomicOrdering::Relaxed);
282    ///             // Fast path for `T: Copy`.
283    ///             unsafe { std::mem::transmute_copy(self) }
284    ///         } else {
285    ///             DEBUG.store(202, AtomicOrdering::Relaxed);
286    ///             Self::from(self.as_ref().clone())
287    ///         }
288    ///     }
289    /// }
290    ///
291    /// #[derive(Clone, Eq, PartialEq, Debug)]
292    /// struct NonCopiable<T>(pub T);
293    ///
294    /// assert_eq!(
295    ///     ArrayLike::from([1, 2, 3]).clone(),
296    ///     ArrayLike::from([1, 2, 3])
297    /// );
298    /// assert_eq!(DEBUG.load(AtomicOrdering::Relaxed), 101);
299    ///
300    /// assert_eq!(
301    ///     ArrayLike::from([NonCopiable(1), NonCopiable(2)]).clone(),
302    ///     ArrayLike::from([NonCopiable(1), NonCopiable(2)])
303    /// );
304    /// assert_eq!(DEBUG.load(AtomicOrdering::Relaxed), 202);
305    /// # }
306    /// ```
307    pub impls_copy_weak: Copy
308);
309define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_clone_weak: Clone);
310define_impls_trait_ignore_lt_fn!(
311    #[auto_doc]
312    /// # Examples
313    ///
314    /// ```rust
315    /// # #[cfg(all(feature = "alloc", feature = "unreliable"))] {
316    ///
317    /// use core::sync::atomic::{AtomicU32, Ordering as AtomicOrdering};
318    /// # use std::sync::Arc;
319    ///
320    /// use try_specialize::unreliable::impls_eq_weak;
321    ///
322    /// #[derive(Clone, Debug)]
323    /// pub struct ArcLike<T> {
324    ///     // ...
325    /// #   inner: Arc<T>,
326    /// }
327    ///
328    /// impl<T> ArcLike<T> {
329    ///     #[inline]
330    ///     fn new(value: T) -> Self {
331    ///         // ...
332    /// #       Self {
333    /// #           inner: Arc::new(value),
334    /// #       }
335    ///     }
336    ///
337    ///     #[inline]
338    ///     fn as_ptr(&self) -> *const T {
339    ///         // ...
340    /// #       Arc::as_ptr(&self.inner)
341    ///     }
342    /// }
343    ///
344    /// impl<T> AsRef<T> for ArcLike<T> {
345    ///     #[inline]
346    ///     fn as_ref(&self) -> &T {
347    ///         // ...
348    /// #       &*self.inner
349    ///     }
350    /// }
351    ///
352    /// impl<T> PartialEq for ArcLike<T>
353    /// where
354    ///     T: PartialEq,
355    /// {
356    ///     #[inline]
357    ///     fn eq(&self, other: &Self) -> bool {
358    ///         // Fast path for `T: Eq`.
359    ///         if impls_eq_weak::<T>() && self.as_ptr() == other.as_ptr() {
360    ///             // Fast path for `T: Eq` if pointers are equal.
361    ///             return true;
362    ///         }
363    ///         self.as_ref() == other.as_ref()
364    ///     }
365    /// }
366    ///
367    /// #[derive(Copy, Clone, Eq, Debug)]
368    /// struct Wrapper<T>(pub T);
369    ///
370    /// static COUNTER: AtomicU32 = AtomicU32::new(0);
371    ///
372    /// impl<T> PartialEq for Wrapper<T>
373    /// where
374    ///     T: PartialEq,
375    /// {
376    ///     #[inline]
377    ///     fn eq(&self, other: &Self) -> bool {
378    ///         let _ = COUNTER.fetch_add(1, AtomicOrdering::Relaxed);
379    ///         self.0 == other.0
380    ///     }
381    /// }
382    ///
383    /// let arc_like1 = ArcLike::new(Wrapper(42_u32));
384    /// let arc_like2 = arc_like1.clone();
385    /// assert_eq!(arc_like1, arc_like2);
386    /// // `u32` implements Eq. Fast path used. Counter not incremented.
387    /// assert_eq!(COUNTER.load(AtomicOrdering::Relaxed), 0);
388    ///
389    /// let arc_like1 = ArcLike::new(Wrapper(123.456_f64));
390    /// let arc_like2 = arc_like1.clone();
391    /// assert_eq!(arc_like1, arc_like2);
392    /// // `f64` doesn't implement Eq. Fast path is not used.
393    /// // Counter incremented.
394    /// assert_eq!(COUNTER.load(AtomicOrdering::Relaxed), 1);
395    /// # }
396    /// ```
397    pub impls_eq_weak: Eq
398);
399define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_partial_eq_weak: PartialEq);
400define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_ord_weak: Ord);
401define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_partial_ord_weak: PartialOrd);
402define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_hash_weak: Hash);
403define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_default_weak: Default);
404define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_debug_weak: Debug);
405define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_display_weak: Display);
406define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_from_str_weak: FromStr);
407define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_iterator_weak: Iterator);
408define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_into_iterator_weak: IntoIterator);
409define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_future_weak: Future);
410define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_into_future_weak: IntoFuture);
411define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_fmt_write_weak: FmtWrite);
412#[cfg(feature = "std")]
413#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
414define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_io_read_weak: IoRead);
415#[cfg(feature = "std")]
416#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
417define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_io_write_weak: IoWrite);
418
419define_impls_trait_ignore_lt_fn!(#[auto_doc] pub impls_lifetime_free_weak: LifetimeFree);
420
421#[cfg(test)]
422mod tests {
423    #[cfg(feature = "alloc")]
424    use alloc::string::String;
425    #[cfg(feature = "alloc")]
426    use alloc::vec::Vec;
427
428    use crate::unreliable::{
429        impls_clone_weak, impls_copy_weak, impls_eq_weak, impls_lifetime_free_weak,
430        impls_partial_eq_weak,
431    };
432
433    #[test]
434    fn test_impls_copy() {
435        #[derive(Copy, Clone)]
436        struct Copiable;
437        #[derive(Clone)]
438        struct Cloneable;
439        struct NonCloneable;
440
441        assert!(impls_copy_weak::<()>());
442        assert!(impls_copy_weak::<u32>());
443        assert!(impls_copy_weak::<f64>());
444
445        assert!(impls_copy_weak::<Copiable>());
446        assert!(!impls_copy_weak::<Cloneable>());
447        assert!(!impls_copy_weak::<NonCloneable>());
448    }
449
450    #[cfg(feature = "alloc")]
451    #[test]
452    fn test_impls_copy_alloc() {
453        assert!(impls_copy_weak::<&String>());
454        assert!(impls_copy_weak::<&Vec<u8>>());
455        assert!(!impls_copy_weak::<String>());
456        assert!(!impls_copy_weak::<Vec<u8>>());
457        assert!(!impls_copy_weak::<&mut String>());
458        assert!(!impls_copy_weak::<&mut Vec<u8>>());
459    }
460
461    #[test]
462    fn test_impls_clone() {
463        #[derive(Copy, Clone)]
464        struct Copiable;
465        #[derive(Clone)]
466        struct Cloneable;
467        struct NonCloneable;
468
469        assert!(impls_clone_weak::<()>());
470        assert!(impls_clone_weak::<u32>());
471        assert!(impls_clone_weak::<f64>());
472
473        assert!(impls_clone_weak::<Copiable>());
474        assert!(impls_clone_weak::<Cloneable>());
475        assert!(!impls_clone_weak::<NonCloneable>());
476    }
477
478    #[test]
479    fn test_impls_eq() {
480        assert!(impls_eq_weak::<()>());
481        assert!(impls_eq_weak::<u32>());
482        assert!(!impls_eq_weak::<f64>());
483    }
484
485    #[cfg(feature = "alloc")]
486    #[test]
487    fn test_impls_eq_alloc() {
488        assert!(impls_eq_weak::<&String>());
489        assert!(impls_eq_weak::<&Vec<u8>>());
490        assert!(impls_eq_weak::<String>());
491        assert!(impls_eq_weak::<Vec<u8>>());
492        assert!(impls_eq_weak::<&mut String>());
493        assert!(impls_eq_weak::<&mut Vec<u8>>());
494    }
495
496    #[test]
497    fn test_impls_partial_eq() {
498        assert!(impls_partial_eq_weak::<()>());
499        assert!(impls_partial_eq_weak::<u32>());
500        assert!(impls_partial_eq_weak::<f64>());
501    }
502
503    #[cfg(feature = "alloc")]
504    #[test]
505    fn test_impls_partial_eq_alloc() {
506        assert!(impls_partial_eq_weak::<&String>());
507        assert!(impls_partial_eq_weak::<&Vec<u8>>());
508        assert!(impls_partial_eq_weak::<String>());
509        assert!(impls_partial_eq_weak::<Vec<u8>>());
510        assert!(impls_partial_eq_weak::<&mut String>());
511        assert!(impls_partial_eq_weak::<&mut Vec<u8>>());
512    }
513
514    #[test]
515    fn test_lifetime_free() {
516        assert!(impls_lifetime_free_weak::<()>());
517        assert!(impls_lifetime_free_weak::<u32>());
518        assert!(impls_lifetime_free_weak::<f64>());
519    }
520
521    #[cfg(feature = "alloc")]
522    #[test]
523    fn test_lifetime_free_alloc() {
524        assert!(!impls_lifetime_free_weak::<&String>());
525        assert!(!impls_lifetime_free_weak::<&Vec<u8>>());
526        assert!(impls_lifetime_free_weak::<String>());
527        assert!(impls_lifetime_free_weak::<Vec<u8>>());
528        assert!(!impls_lifetime_free_weak::<&mut String>());
529        assert!(!impls_lifetime_free_weak::<&mut Vec<u8>>());
530    }
531}