-
-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathdoes_impl.rs
108 lines (101 loc) · 3.79 KB
/
does_impl.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/// Returns `true` if the type does implement a logical trait expression.
///
/// # Examples
///
/// One can mimic `assert_impl!` using this macro:
///
/// ```
/// # #[macro_use] extern crate static_assertions; fn main() {}
/// const CONDITION: bool = does_impl!(u32: From<u8>);
///
/// const_assert!(CONDITION);
/// ```
#[macro_export(local_inner_macros)]
macro_rules! does_impl {
($ty:ty: $($trait_expr:tt)+) => {
_does_impl!($ty: $($trait_expr)+).value()
};
}
/// Returns `True` or `False` depending on whether the given type implements the
/// given trait boolean expression. Can be used in const contexts if it doesn't
/// depend on outer generic parameters.
///
/// This is the core of `assert_impl`.
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! _does_impl {
($ty:ty: $($rest:tt)*) => {{
#[allow(unused_imports)]
use $crate::{
_bool::{True, False},
_core::{marker::PhantomData, ops::Deref},
};
// Fallback trait that returns false if the type does not implement a
// given trait.
trait DoesntImpl {
const DOES_IMPL: False = False;
}
impl<T: ?Sized> DoesntImpl for T {}
// Construct an expression using `True`/`False` and their operators,
// that corresponds to the provided expression.
*_does_impl!(@boolexpr($ty,) $($rest)*)
}};
(@boolexpr($($args:tt)*) ($($expr:tt)*)) => {
_does_impl!(@boolexpr($($args)*) $($expr)*)
};
(@boolexpr($($args:tt)*) !($($expr:tt)*)) => {
_does_impl!(@boolexpr($($args)*) $($expr)*).not()
};
(@boolexpr($($args:tt)*) ($($left:tt)*) | $($right:tt)*) => {{
let left = _does_impl!(@boolexpr($($args)*) $($left)*);
let right = _does_impl!(@boolexpr($($args)*) $($right)*);
left.or(right)
}};
(@boolexpr($($args:tt)*) ($($left:tt)*) & $($right:tt)*) => {{
let left = _does_impl!(@boolexpr($($args)*) $($left)*);
let right = _does_impl!(@boolexpr($($args)*) $($right)*);
left.and(right)
}};
(@boolexpr($($args:tt)*) !($($left:tt)*) | $($right:tt)*) => {{
_does_impl!(@boolexpr($($args)*) (!($($left)*)) | $($right)*)
}};
(@boolexpr($($args:tt)*) !($($left:tt)*) & $($right:tt)*) => {{
_does_impl!(@boolexpr($($args)*) (!($($left)*)) & $($right)*)
}};
(@boolexpr($($args:tt)*) !$left:ident | $($right:tt)*) => {{
_does_impl!(@boolexpr($($args)*) !($left) | $($right)*)
}};
(@boolexpr($($args:tt)*) !$left:ident & $($right:tt)*) => {{
_does_impl!(@boolexpr($($args)*) !($left) & $($right)*)
}};
(@boolexpr($($args:tt)*) $left:ident | $($right:tt)*) => {
_does_impl!(@boolexpr($($args)*) ($left) | $($right)*)
};
(@boolexpr($($args:tt)*) $left:ident & $($right:tt)*) => {{
_does_impl!(@boolexpr($($args)*) ($left) & $($right)*)
}};
(@boolexpr($($args:tt)*) !$expr:ident) => {
_does_impl!(@boolexpr($($args)*) !($expr))
};
(@boolexpr($($args:tt)*) !$expr:path) => {
_does_impl!(@boolexpr($($args)*) !($expr))
};
(@boolexpr($($args:tt)*) $expr:ident) => {
_does_impl!(@base($($args)*) $expr)
};
(@boolexpr($($args:tt)*) $expr:path) => {
_does_impl!(@base($($args)*) $expr)
};
(@base($ty:ty, $($args:tt)*) $($trait:tt)*) => {{
// Base case: computes whether `ty` implements `trait`.
struct Wrapper<T: ?Sized>(PhantomData<T>);
#[allow(dead_code)]
impl<T: ?Sized + $($trait)*> Wrapper<T> {
const DOES_IMPL: True = True;
}
// If `$type: $trait`, the `_does_impl` inherent method on `Wrapper`
// will be called, and return `True`. Otherwise, the trait method will
// be called, which returns `False`.
&<Wrapper<$ty>>::DOES_IMPL
}};
}