Skip to main content

token_cell/
runtime_token_support.rs

1use core::fmt::Debug;
2
3/// A trait for types that can serve as unique identifiers for runtime tokens.
4///
5/// An `Identifier` must be able to be represented by an atomic type that
6/// can be incremented to produce the next identifier.
7pub trait Identifier: Eq + Debug + Copy + Send + Sync {
8    /// The atomic counterpart to this identifier type.
9    type AtomicType: RollingCounter<NonAtomic = Self>;
10    /// Whether or not comparison between two distinct token could yield `eq`.
11    type ComparisonMaySpuriouslyEq: crate::core::sealed::Boolean;
12}
13
14/// A trait for types that can be used as rolling counters. It is only intended for the atomic counterparts to [`Identifier`] types.
15pub trait RollingCounter {
16    /// The starting value of the rolling counter.
17    const DEFAULT: Self;
18    /// The non-atomic identifier type associated with this atomic type.
19    type NonAtomic;
20    /// Atomically increments the value and returns the previous value.
21    fn next(&self) -> Self::NonAtomic;
22}
23
24macro_rules! impl_identifier {
25    (comp u64) => {
26        crate::core::False
27    };
28    (comp u128) => {
29        crate::core::False
30    };
31    (comp $id: ty) => {
32        crate::core::False
33    };
34    ($id: ty, $atom: ty) => {
35        impl Identifier for $id {
36            type AtomicType = $atom;
37            type ComparisonMaySpuriouslyEq = impl_identifier!(comp $id);
38        }
39        // This is an old false-positive:
40        // - `DEFAULT` is a `const` because `const fn` in traits is not stable yet.
41        // - That const is copied into a `static` that allows interior mutability, and only ever used that way
42        #[rustversion::attr(before(1.78), allow(clippy::declare_interior_mutable_const))]
43        impl RollingCounter for $atom {
44            const DEFAULT: Self = Self::new(0);
45            type NonAtomic = $id;
46            fn next(&self) -> Self::NonAtomic {
47                self.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
48            }
49        }
50    };
51}
52
53impl_identifier!(u8, portable_atomic::AtomicU8);
54impl_identifier!(u16, portable_atomic::AtomicU16);
55impl_identifier!(u32, portable_atomic::AtomicU32);
56impl_identifier!(u64, portable_atomic::AtomicU64);
57impl_identifier!(u128, portable_atomic::AtomicU128);
58
59/// The comparison error for runtime tokens.
60///
61/// Such an error being returned implies a massive bug in the calling code, as it implies that
62/// said code failed to track which instance of a [`runtime_token`] was used to construct a cell,
63/// and thus failed to ensure that the token was used correctly.
64///
65/// Unless the token is backed by an integer with sufficient size to guarantee that the program
66/// never constructs multiple tokens with a same identifier, your program SHOULD NOT rely on
67/// token identifiers being unique for soundness.
68#[derive(Debug, Clone, Copy)]
69pub struct IdMismatch<T: Identifier> {
70    /// The identifier of the token the cell was expecting.
71    pub cell: T,
72    /// The identifier of the token that was used to attempt accessing the cell's contents.
73    pub token: T,
74}
75
76impl<T: Identifier> ::core::fmt::Display for IdMismatch<T> {
77    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
78        write!(f, "{:?}", self)
79    }
80}
81
82/// Produces tokens that are also checked at runtime, ensuring that a [`TokenCell`] is never accidentally used with another instance of the same token type.
83///
84/// Each token of a given type generated by [`runtime_token`] being identified by a `u16` by default, constructing over 65536 instances may mean that distinct token instances share a same identifier,
85/// and may unlock each other's cells.
86///
87/// This means that if you accidentally use the wrong instance of a runtime token on a cell, you are likely but not _guaranteed_ to be protected by the identifier
88///
89/// You can chose the size of your tokens by calling `runtime_token!(pub MyToken: uX)`.
90/// `u64` has more overhead than the default `u16`, but guarantees that you will never accidentally use the wrong instance of a token undetected;
91/// therefore, runtime tokens that use `u64` as their backing allow you to use the [safe](crate::core::TokenCellTrait) access methods to [`TokenCell`]
92///
93/// [`TokenCell`]: crate::core::TokenCell
94#[macro_export]
95macro_rules! runtime_token {
96    ($(#[$meta: meta])* $vis: vis $id: ident) => {
97        runtime_token!($(#[meta])* $vis $id: u16);
98    };
99    ($(#[$meta: meta])* $vis: vis $id: ident: $ty: ty) => {
100        $crate::paste! {
101            $vis use [<__ $id _mod__ >]::$id;
102            #[allow(nonstandard_style)]
103            mod [<__ $id _mod__ >] {
104                use core::convert::Infallible;
105                use $crate::core::UnscopedToken;
106                use $crate::runtime_token_support::{Identifier, IdMismatch, RollingCounter};
107
108                static COUNTER: <$ty as Identifier>::AtomicType = <<$ty as Identifier>::AtomicType as RollingCounter>::DEFAULT;
109
110                $(#[$meta])*
111                #[derive(::core::fmt::Debug)]
112                pub struct $id($ty);
113
114                impl UnscopedToken for $id {
115                    type ConstructionError = Infallible;
116                    fn try_new() -> Result<Self, Self::ConstructionError> {
117                        let id = COUNTER.next();
118                        Ok(Self(id))
119                    }
120                }
121                impl $crate::core::TokenTrait for $id {
122                    type ComparisonMaySpuriouslyEq = $crate::core::False;
123                    type RunError = Infallible;
124                    type Identifier = $ty;
125                    type ComparisonError = IdMismatch<$ty>;
126                    type Branded<'a> = Self;
127                    fn with_token<R, F: for<'a> FnOnce(Self::Branded<'a>) -> R>(f: F) -> Result<R, Self::RunError> {
128                        Ok(f(Self::new()))
129                    }
130                    fn identifier(&self) -> Self::Identifier { self.0 }
131                    fn compare(&self, id: &Self::Identifier) -> Result<(), Self::ComparisonError> {
132                        if *id == self.0 { Ok(()) } else { Err(IdMismatch { cell: *id, token: self.0 }) }
133                    }
134                }
135            }
136        }
137    };
138    ($($(#[$meta: meta])* $vis: vis $id: ident),*) => {
139        $($crate::runtime_token!($(#[meta])* $vis $id);)*
140    };
141    ($($(#[$meta: meta])* $vis: vis $id: ident: $ty: ty),*) => {
142        $($crate::runtime_token!($(#[meta])* $vis $id: $ty);)*
143    };
144}