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() {}