intid_core/trusted.rs
1//! Since [`IntegerId`] and [`IntegerIdContiguous`] are safe traits,
2//! it is not possible for unsafe code to rely on them for correctness.
3//!
4//! There is an exception for [`IntegerId::from_int_unchecked`],
5//! which is required to accept any value returned from [`IntegerId::to_int`].
6//! If [`IntegerIdContiguous`] is implemented,
7//! the `from_int_unchecked` function must also accept all values in the range `Self::MIN_ID..=Self::MAX_ID`.
8//!
9//! These assumptions allow zero-cost conversions from `T::Int` to `T`,
10//! but do not affect fully-safe implementations of `IntegerId`
11//! where [`IntegerId::from_int_unchecked`] simply delegates to [`IntegerId::from_int`].
12//!
13//! Because it is a fully safe function in a fully safe trait,
14//! there is no way for us to trust its results.
15//!
16//! In order to work around this, we provide special "trust tokens".
17//! These tokens are associated constants on the trait,
18//! so that they are always resolved at compile time.
19//! These require invoking an `unsafe` code to construct the token,
20//! to prevent fully safe code from being able to trigger undefined behavior.
21//!
22//! [`IntegerIdContiguous`]: crate::IntegerIdContiguous
23
24use core::marker::PhantomData;
25
26use crate::IntegerId;
27
28/// Indicates that an [`IntegerId`] unsafely guarantees that the result of [`IntegerId::to_int`]
29/// will always fall in the range `IntegerId::MIN_INT..=IntegerId::MAX_ID`.
30///
31/// Also guarantees that the [`IntegerId::to_int`] is implemented in the expected manner.
32///
33/// Just because the type implements something like [`bytemuck::Contiguous`] does not mean
34/// that it is valid to create the token, as [`IntegerId::to_int`] could still be implemented incorrectly.
35#[derive(Copy, Clone)]
36pub struct TrustedRangeToken<T: IntegerId> {
37 marker: PhantomData<&'static T>,
38}
39impl<T: IntegerId> TrustedRangeToken<T> {
40 /// Promise that the type `T` satisfies the appropriate correctness requirements.
41 ///
42 /// # Safety
43 /// If the [`IntegerId`] does not meet the requirements,
44 /// this is immediate undefined behavior (similar to constructing a `!` type).
45 pub const unsafe fn assume_valid() -> Self {
46 TrustedRangeToken {
47 marker: PhantomData,
48 }
49 }
50
51 /// Promise that the type `T` satisfies the appropriate correctness whenever `U` promises to.
52 ///
53 /// This function is helpful for implementing newtype wrappers around an arbitrary inner type.
54 ///
55 /// This is equivalent to `T::TRUSTED_RANGE::map(|| unsafe { TrustedRangeToken::assume_valid() })`,
56 /// but works in a `const` context.
57 ///
58 /// # Safety
59 /// You must ensure that `U` can be trusted with the requirements of [`TrustedRangeToken`]
60 /// whenever `U` meets those same requirements.
61 pub const unsafe fn assume_valid_if<U: IntegerId>() -> Option<Self> {
62 if <U as IntegerId>::TRUSTED_RANGE.is_some() {
63 // SAFETY: Caller guarantees that T is trusted whenever U is
64 Some(unsafe { TrustedRangeToken::<T>::assume_valid() })
65 } else {
66 None
67 }
68 }
69}