proof_token/
proof-token.rs

1// This example demonstrates using `Target` to create a "proof token type", a token that
2// demonstrates that particular target features have already been detected, and that those features
3// can be used safely.
4
5#![allow(unused, unused_macros, unused_imports)]
6
7use target_features::Target;
8
9/// Make sure proof tokens can't be improperly constructed
10mod unconstructible {
11    pub struct Unconstructible(());
12    impl Unconstructible {
13        pub unsafe fn new() -> Self {
14            Self(())
15        }
16    }
17}
18use unconstructible::Unconstructible;
19
20/// Proof of target feature support.
21///
22/// # Safety
23/// The type must be implemented such that it's impossible to safely construct without ensuring the
24/// specified target features are supported.
25unsafe trait Proof: Sized {
26    /// The proven target
27    const TARGET: Target;
28
29    /// Detect the support for the target features
30    fn detect() -> Option<Self>;
31
32    /// Assume the target features are supported
33    ///
34    /// # Safety
35    /// Calling this is undefined if the target features are not supported
36    unsafe fn assume() -> Self;
37}
38
39/// Make a proof token type for a particular set of features
40macro_rules! make_target_proof {
41    { $vis:vis struct $proof:ident($($feature:tt),*); } => {
42        $vis struct $proof(Unconstructible);
43
44        unsafe impl Proof for $proof {
45            // Build on the already-known target features
46            const TARGET: Target = target_features::CURRENT_TARGET$(.with_feature_str($feature))*;
47
48            fn detect() -> Option<Self> {
49                if true $(&& is_x86_feature_detected!($feature))* {
50                    unsafe { Some(Self::assume()) }
51                } else {
52                    None
53                }
54            }
55
56            unsafe fn assume() -> Self {
57                Self(Unconstructible::new())
58            }
59        }
60    }
61}
62
63/// A function that can only be called with the "avx" feature, or panics otherwise.
64#[cfg(target_arch = "x86_64")]
65fn safe_avx_fn<P: Proof>(_: P) {
66    #[target_feature(enable = "avx")]
67    unsafe fn unsafe_avx_fn() {
68        println!("called an avx function")
69    }
70
71    // Future improvements to const generics might make it possible to assert this at compile time.
72    // Since P::TARGET is const, this assert disappears if the required features are present.
73    assert!(
74        P::TARGET.supports_feature_str("avx"),
75        "avx feature not supported"
76    );
77    unsafe { unsafe_avx_fn() }
78}
79
80#[cfg(target_arch = "x86_64")]
81fn main() {
82    // The function can be called with the exact features
83    make_target_proof! {
84        struct Avx("avx");
85    }
86    if let Some(proof) = Avx::detect() {
87        safe_avx_fn(proof);
88    }
89
90    // The function can also be called with a target that implies the required features
91    make_target_proof! {
92        struct Avx2("avx2");
93    }
94    if let Some(proof) = Avx2::detect() {
95        safe_avx_fn(proof);
96    }
97
98    // This panics, unless compiled with something like `-Ctarget-feature=+avx`
99    make_target_proof! {
100        struct Aes("aes");
101    }
102    if let Some(proof) = Aes::detect() {
103        safe_avx_fn(proof);
104    }
105}
106
107#[cfg(not(target_arch = "x86_64"))]
108fn main() {}