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 ¬_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}