cisness/
lib.rs

1// This uses markdown link resolution to allow links in the readme to work both on crates.io and
2// within docs.rs
3//! [cismute]: `cismute`
4//! [live-witness]: `LiveWitness`
5//! [typeid]: `core::any::TypeId`
6#![doc = include_str!("../README.md")]
7#![cfg_attr(not(test), no_std)]
8
9use core::{hint::unreachable_unchecked, marker::PhantomData};
10
11use cismute::Cismutable;
12
13macro_rules! cisunreachable {
14    () => {
15        unreachable!("cismute live-witness used in code path where types were not actually the same/cismutable")
16    };
17}
18
19/// Live witness type - indicates that you have for some region of code ensured that it *will not
20/// actually be executed* if `U` is not the same type as `T`. If it is, then expect rapid panics.
21///
22/// Generally you should try to invoke this in places where it is trivially proven that the type
23/// IDs of the types are already shown to be the same - this should allow the compiler to optimise
24/// away basically everything.
25///
26/// This type attempts to expose the full power of [`cismute`] to it's users, with the caveat of
27/// panicking if the types are not actually the same when the relevant transmutations are executed
28/// (this should be optimised out in basically all cases, as long as you're actually doing some
29/// kind of check like typeid matching to ensure the types are really the same). If you really
30/// really need it, there are ways to do `unreachable_unchecked`-style transmutation.
31pub struct LiveWitness<Ty, Is>(PhantomData<Ty>, PhantomData<Is>);
32
33impl<U, T> Clone for LiveWitness<U, T> {
34    #[inline(always)]
35    fn clone(&self) -> Self {
36        *self
37    }
38}
39impl<U, T> Copy for LiveWitness<U, T> {}
40
41impl<Ty, Is> LiveWitness<Ty, Is> {
42    /// A live witness that says two types are the same also works in the reverse direction. This
43    /// function lets you take any live witness and swap the types so you can do transmutation in
44    /// the other direction
45    #[inline(always)]
46    pub const fn inverse(self) -> LiveWitness<Is, Ty> {
47        LiveWitness::only_executed_if_same()
48    }
49
50    /// This function creates a live witness, assuring that the codepath using this type will not
51    /// be invoked unless `Ty` and `Is` are the same.
52    #[inline(always)]
53    pub const fn only_executed_if_same() -> Self {
54        Self(PhantomData, PhantomData)
55    }
56
57    /// Analogous to [`cismute::value`], but using a live witness to assure that the types are the
58    /// same. This will never check at runtime if they are the same even if the compiler can't
59    /// prove they are, and will cause undefined behaviour if the codepath you thought would never
60    /// be executed when the types aren't the same, is actually executed when they are not the same.
61    ///
62    /// # Safety
63    /// You must know **for certain** that this function will never be executed when `Ty` and `Is`
64    /// are not the same, or this may cause highly unpredictable undefined behaviour. Prefer
65    /// [`LiveWitness::value`] unless you know for sure.
66    #[inline(always)]
67    pub unsafe fn value_unchecked<'a, RefTy, RefIs>(&self, from: RefTy) -> RefIs
68    where
69        Ty: 'static,
70        Is: 'static,
71        RefTy: Cismutable<'a, Ty, Is, RefIs>,
72    {
73        match cismute::value(from) {
74            Ok(v) => v,
75            Err(_) => unreachable_unchecked(),
76        }
77    }
78
79    /// Analogous to [`cismute::value`], but using a live witness to assure that the types are the
80    /// same (panics if they are not).
81    ///
82    /// It may be easier on type-deduction to invoke one of several other functions that are
83    /// slightly less general:
84    /// * [`LiveWitness::owned`]
85    /// * [`LiveWitness::reference`]
86    /// * [`LiveWitness::mutable`]
87    #[inline(always)]
88    pub fn value<'a, RefTy, RefIs>(&self, from: RefTy) -> RefIs
89    where
90        Ty: 'static,
91        Is: 'static,
92        RefTy: Cismutable<'a, Ty, Is, RefIs>,
93    {
94        match cismute::value(from) {
95            Ok(v) => v,
96            Err(_) => {
97                cisunreachable!()
98            }
99        }
100    }
101
102    /// Analogous to [`cismute::owned`], but using a live witness to assure that the types are the
103    /// same. This will cause undefined behaviour if actually invoked when the types are not the
104    /// same - consider using [`LiveWitness::owned`] if you can
105    /// # Safety
106    /// Ensure this is never actually invoked when the types are not the same - i.e, the codepath
107    /// on which the [`LiveWitness`] is created is never run if the types are different.
108    #[inline(always)]
109    pub unsafe fn owned_unchecked(&self, from: Ty) -> Is
110    where
111        Ty: 'static,
112        Is: 'static,
113    {
114        match cismute::owned(from) {
115            Ok(v) => v,
116            Err(_) => unreachable_unchecked(),
117        }
118    }
119
120    /// Analogous to [`cismute::owned`], but using a live witness to assure that the types are the
121    /// same. This will panic if actually invoked when the types are not the same.
122    #[inline(always)]
123    pub fn owned(&self, from: Ty) -> Is
124    where
125        Ty: 'static,
126        Is: 'static,
127    {
128        match cismute::owned(from) {
129            Ok(v) => v,
130            Err(_) => cisunreachable!(),
131        }
132    }
133
134    /// Analogous to [`cismute::reference`], but using a live witness to assure that the types are
135    /// the same. This will cause undefined behaviour if actually invoked when the types are not
136    /// the same - consider using [`LiveWitness::reference`] if you can.
137    /// # Safety
138    /// Ensure this is never invoked if the types are not the same
139    #[inline(always)]
140    pub unsafe fn reference_unchecked<'a>(&self, from: &'a Ty) -> &'a Is
141    where
142        Ty: 'static,
143        Is: 'static,
144        &'a Ty: Cismutable<'a, Ty, Is, &'a Is>,
145    {
146        match cismute::reference(from) {
147            Ok(v) => v,
148            Err(_) => unreachable_unchecked(),
149        }
150    }
151
152    /// Analogous to [`cismute::reference`], but using a live witness to assure that the types are
153    /// the same. This will `panic!()` if the types are not actually the same.
154    #[inline(always)]
155    pub fn reference<'a>(&self, from: &'a Ty) -> &'a Is
156    where
157        Ty: 'static,
158        Is: 'static,
159        &'a Ty: Cismutable<'a, Ty, Is, &'a Is>,
160    {
161        match cismute::reference(from) {
162            Ok(v) => v,
163            Err(_) => cisunreachable!(),
164        }
165    }
166
167    /// Analogous to [`cismute::mutable`], but using a live witness to assure that the types are
168    /// the same. This will cause undefined behaviour if actually invoked when the types are not
169    /// the same - consider using [`LiveWitness::mutable`] if you can.
170    /// # Safety
171    /// Ensure this is never invoked if the types are not the same
172    #[inline(always)]
173    pub unsafe fn mutable_unchecked<'a>(&self, from: &'a mut Ty) -> &'a mut Is
174    where
175        Ty: 'static,
176        Is: 'static,
177        &'a mut Ty: Cismutable<'a, Ty, Is, &'a mut Is>,
178    {
179        match cismute::mutable(from) {
180            Ok(v) => v,
181            Err(_) => unreachable_unchecked(),
182        }
183    }
184
185    /// Analogous to [`cismute::mutable`], but using a live witness to assure that the types are
186    /// the same. This will `panic!()` if the types are not actually the same.
187    #[inline(always)]
188    pub fn mutable<'a>(&self, from: &'a mut Ty) -> &'a mut Is
189    where
190        Ty: 'static,
191        Is: 'static,
192        &'a mut Ty: Cismutable<'a, Ty, Is, &'a mut Is>,
193    {
194        match cismute::mutable(from) {
195            Ok(v) => v,
196            Err(_) => cisunreachable!(),
197        }
198    }
199}
200
201// This Source Code Form is subject to the terms of the Mozilla Public
202// License, v. 2.0. If a copy of the MPL was not distributed with this
203// file, You can obtain one at http://mozilla.org/MPL/2.0/.