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}