type_equalities/
lib.rs

1// Copyright (c) 2021 t WorldSEnder
2// Licensed under the Apache License, Version 2.0
3// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
4// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
5// at your option. All files in the project carrying such
6// notice may not be copied, modified, or distributed except
7// according to those terms.
8#![cfg_attr(feature = "test-for-type-equality", feature(specialization))]
9#![cfg_attr(unstable_feature, feature(const_fn_trait_bound))]
10#![cfg_attr(unstable_feature, feature(doc_cfg))]
11#![warn(missing_docs, rustdoc::missing_crate_level_docs)]
12#![no_std]
13//! Implements [`TypeEq`] that can be passed around and used at runtime to safely coerce values,
14//! references and other structures dependending on these types.
15//!
16//! The equality type is zero-sized, and the coercion should optimize to a no-op in all cases.
17//!
18//! This crate is `![no_std]`. You can optionally turn off the `alloc` feature.
19
20extern crate core;
21use core::marker::PhantomData;
22
23#[cfg(feature = "alloc")]
24extern crate alloc;
25#[cfg(feature = "alloc")]
26use alloc::boxed::Box;
27
28use details::*;
29use kernel::{refl as refl_kernel, use_eq as use_kernel_eq, TheEq};
30use type_functions::*;
31
32/// Equality at a constraint level, as a type alias. Reflexivity holds.
33///
34/// # Example
35///
36/// Note that due to the rust type checker, coercions are not as simple as they
37/// might look.
38///
39/// ```compile_fail
40/// # use type_equalities::IsEqual;
41/// // Trying to implement coerce like this fails!
42/// fn foo<U, T: IsEqual<U>>(t: T) -> U { t }
43/// assert_eq!(foo::<u32, u32>(42), 42)
44/// //   |
45/// // 6 | fn foo<U, T: IsEqual<U>>(t: T) -> U { t }
46/// //   |        -  -                       -   ^ expected type parameter `U`, found type parameter `T`
47/// //   |        |  |                       |
48/// //   |        |  found type parameter    expected `U` because of return type
49/// //   |        expected type parameter
50/// ```
51///
52/// But the following works correctly:
53///
54/// ```
55/// # use type_equalities::{IsEqual, coerce, trivial_eq};
56/// fn foo<U, T: IsEqual<U>>(t: T) -> U { trivial_eq().coerce(t) }
57/// assert_eq!(foo::<u32, u32>(42), 42)
58/// ```
59pub trait IsEqual<U: ?Sized>: AliasSelf<Alias = U> {}
60impl<T: ?Sized, U: ?Sized> IsEqual<U> for T where T: AliasSelf<Alias = U> {}
61
62/// Evidence of the equality `T == U` as a zero-sized type.
63///
64/// ```
65/// # use type_equalities::TypeEq;
66/// # type T = ();
67/// # type U = ();
68/// assert_eq!(core::mem::size_of::<TypeEq<T, U>>(), 0);
69/// ```
70///
71/// It is important to note that the `TypeEq` is [invariant]
72/// in both arguments.
73///
74/// ```compile_fail
75/// # use type_equalities::TypeEq;
76/// fn coerce_lt<'a, 'b: 'a, T>(eq: TypeEq<&'b T, &'b T>)
77///     -> TypeEq<&'b T, &'a T>
78/// {
79///     eq
80/// }
81/// ```
82///
83/// ```compile_fail
84/// # use type_equalities::TypeEq;
85/// fn coerce_lt_inv<'a, 'b: 'a, T>(eq: TypeEq<&'a T, &'a T>)
86///     -> TypeEq<&'a T, &'b T>
87/// {
88///     eq
89/// }
90/// ```
91///
92/// Unsizing also does not work for TypeEq.
93///
94/// ```compile_fail
95/// fn coerce_dyn<T: core::fmt::Debug>(eq: &TypeEq<T, T>)
96///     -> &TypeEq<T, dyn core::fmt::Debug>
97/// {
98///     eq
99/// }
100/// ```
101///
102/// [invariant]: https://doc.rust-lang.org/nomicon/subtyping.html#variance
103pub struct TypeEq<T: ?Sized, U: ?Sized> {
104    _inner: TheEq<T, U>,
105}
106
107impl<T: ?Sized, U: ?Sized> Clone for TypeEq<T, U> {
108    fn clone(&self) -> Self {
109        *self
110    }
111}
112impl<T: ?Sized, U: ?Sized> Copy for TypeEq<T, U> {}
113
114/// Construct evidence of the reflexive equality `T == T`.
115///
116/// There is also a constructor-like version of this, [`TypeEq::refl`].
117pub const fn refl<T: ?Sized>() -> TypeEq<T, T> {
118    TypeEq {
119        _inner: refl_kernel(),
120    }
121}
122
123/// Construct evidence of `TypeEq<T, U>` under the constraint `T: IsEqual<U>`.
124///
125/// There is also a receiver version of this, [`TypeEq::trivial`].
126///
127/// Note quite as trivial to implement as it might appear, since we're fighting
128/// the type checker a bit.
129///
130/// **Note**: this function is `const` only on nightly, since it depends on the
131/// [`const_fn_trait_bound`] feature.
132///
133/// [`const_fn_trait_bound`]: https://doc.rust-lang.org/stable/unstable-book/language-features/const-fn-trait-bound.html
134#[rustversion::attr(nightly, const)]
135pub fn trivial_eq<T: ?Sized, U: ?Sized>() -> TypeEq<T, U>
136where
137    T: IsEqual<U>,
138{
139    const fn refl_alias<T: ?Sized>() -> TypeEq<T, <T as AliasSelf>::Alias> {
140        refl()
141    }
142    refl_alias()
143}
144
145/// Coerce a value of type `T` to a value of type `U`, given evidence that `T == U`.
146///
147/// Note that this is operationally a no-op.
148///
149/// There is also a receiver version of this, [`TypeEq::coerce`].
150///
151/// # Examples
152///
153/// ```
154/// # use type_equalities::{coerce, refl};
155/// assert_eq!(coerce(42, refl()), 42);
156/// ```
157#[inline(always)]
158pub fn coerce<T, U>(t: T, ev: TypeEq<T, U>) -> U {
159    substitute::<_, _, IdF>(t, ev)
160}
161
162/// Coerce a value of type `Box<T>` to a value of type `Box<U>`, given evidence that `T == U`.
163///
164/// # Examples
165///
166/// ```
167/// # use type_equalities::{coerce_box, refl};
168/// assert_eq!(*coerce_box(Box::new(42), refl()), 42);
169/// ```
170#[cfg(feature = "alloc")]
171#[rustversion::attr(nightly, doc(cfg(feature = "alloc")))]
172#[inline]
173pub fn coerce_box<T: ?Sized, U: ?Sized>(t: Box<T>, ev: TypeEq<T, U>) -> Box<U> {
174    substitute::<_, _, BoxF>(t, ev)
175}
176
177/// Coerce a value of type `&T` to a value of type `&U`, given evidence that `T == U`.
178///
179/// # Examples
180///
181/// ```
182/// # use type_equalities::{coerce_ref, refl};
183/// assert_eq!(*coerce_ref(&42, refl()), 42);
184/// ```
185#[inline]
186pub fn coerce_ref<T: ?Sized, U: ?Sized>(t: &T, ev: TypeEq<T, U>) -> &U {
187    substitute::<_, _, RefF>(t, ev)
188}
189
190/// Coerce a value of type `&mut T` to a value of type `&mut U`, given evidence that `T == U`.
191///
192/// # Examples
193///
194/// ```
195/// # use type_equalities::{coerce_ref, refl};
196/// assert_eq!(*coerce_ref(&mut 42, refl()), 42);
197/// ```
198#[inline]
199pub fn coerce_mut<T: ?Sized, U: ?Sized>(t: &mut T, ev: TypeEq<T, U>) -> &mut U {
200    substitute::<_, _, MutRefF>(t, ev)
201}
202
203/// Our workhorse for most of the other coerce implementations, lifting the equality through
204/// an arbitrary [`TypeFunction`]. Do consider using this before writing a custom Consumer.
205///
206/// There is also a receiver version of this, [`TypeEq::substitute`].
207#[inline(always)]
208pub fn substitute<T: ?Sized, U: ?Sized, F: TypeFunction<T> + TypeFunction<U>>(
209    t: ApF<F, T>,
210    ev: TypeEq<T, U>,
211) -> ApF<F, U>
212where
213    ApF<F, T>: Sized,
214    ApF<F, U>: Sized,
215{
216    struct FunCoercer<T: ?Sized, F: TypeFunction<<T as AliasSelf>::Alias>>(F::Result);
217
218    unsafe impl<T: ?Sized, U: ?Sized, F: TypeFunction<T> + TypeFunction<U>> Consumer<T, U>
219        for FunCoercer<T, F>
220    where
221        ApF<F, U>: Sized,
222    {
223        type Result = ApF<F, U>;
224        fn consume_eq(&self) -> Self::Result
225        where
226            T: IsEqual<U>,
227        {
228            let self_ = unsafe { core::ptr::read(self as *const Self) };
229            self_.0
230        }
231    }
232    let con: FunCoercer<T, F> = FunCoercer(t);
233    ev.use_eq(con)
234}
235
236impl<T: ?Sized> TypeEq<T, T> {
237    /// Same as [`crate::refl`].
238    pub const fn refl() -> TypeEq<T, T> {
239        self::refl()
240    }
241}
242
243impl<T: ?Sized, U: ?Sized> TypeEq<T, U> {
244    /// Same as [`crate::trivial_eq`].
245    ///
246    /// **Note**: this function is `const` only on nightly, since it depends on the
247    /// [`const_fn_trait_bound`] feature.
248    ///
249    /// [`const_fn_trait_bound`]: https://doc.rust-lang.org/stable/unstable-book/language-features/const-fn-trait-bound.html
250    #[rustversion::attr(nightly, const)]
251    pub fn trivial() -> Self
252    where
253        T: IsEqual<U>,
254    {
255        self::trivial_eq()
256    }
257    /// Same as [`crate::substitute`].
258    #[inline(always)]
259    pub fn substitute<F: TypeFunction<T> + TypeFunction<U>>(self, t: ApF<F, T>) -> ApF<F, U>
260    where
261        ApF<F, T>: Sized,
262        ApF<F, U>: Sized,
263    {
264        self::substitute::<_, _, F>(t, self)
265    }
266    /// Same as [`crate::coerce`]. Note that this is operationally a no-op.
267    ///
268    /// # Examples
269    ///
270    /// ```
271    /// # use type_equalities::refl;
272    /// assert_eq!(refl().coerce(42), 42);
273    /// ```
274    #[inline(always)]
275    pub fn coerce(self, t: T) -> U
276    where
277        T: Sized,
278        U: Sized,
279    {
280        self::coerce(t, self)
281    }
282    /// Lift the type equality through any [`TypeFunction`]
283    pub fn lift_through<F: TypeFunction<T> + TypeFunction<U>>(
284        self,
285    ) -> TypeEq<ApF<F, T>, ApF<F, U>> {
286        type R<T, F> = ComposeF<LoefIdF<ApF<F, T>>, F>;
287        self.substitute::<R<T, F>>(refl())
288    }
289    /// Get the inverse equality. `T == U  ==>  U == T`
290    pub fn invert(self) -> TypeEq<U, T> {
291        self.substitute::<LoefIdFlippedF<T>>(refl())
292    }
293    /// Apply transitivity. `T == U & U == V  ==>  T == V`
294    pub fn trans<V: ?Sized>(self, rhs: TypeEq<U, V>) -> TypeEq<T, V> {
295        rhs.substitute::<LoefIdF<T>>(self)
296    }
297}
298
299impl<T: ?Sized, U: ?Sized> TypeEq<T, U> {
300    /// Use the observed equality to call the consumer to compute a result.
301    ///
302    /// Consider using either [`TypeEq::coerce`] or [`TypeEq::lift_through`] first.
303    #[inline(always)]
304    pub fn use_eq<C: Consumer<T, U>>(self, c: C) -> C::Result {
305        use_kernel_eq(self._inner, c)
306    }
307}
308
309/// Details for primitively consuming an equality.
310pub mod details {
311    use crate::IsEqual;
312
313    /// A consumer recives evidence of a type equality `T == U` and computes a result.
314    ///
315    /// # Safety
316    ///
317    /// See the docs of [`consume_eq`] for further safety.
318    ///
319    /// [`consume_eq`]: Self::consume_eq
320    pub unsafe trait Consumer<T: ?Sized, U: ?Sized> {
321        /// The result type returned from [`Consumer::consume_eq`].
322        type Result;
323        /// The strange `where` clause enables the consumer to observe that:
324        /// - `T == <T as AssocSelf>::Alias` by the implementation of `AssocSelf`
325        /// - `T::Alias == U`
326        ///
327        /// Directly writing `T = U` is currently not possible, as tracked by [issue #20041].
328        ///
329        /// [`AliasSelf`] is a workaround, to make it easier for implementors to construct their
330        /// own `Consumer`s.
331        ///
332        /// # Safety
333        ///
334        /// `self` is passed as a const ref. Using [`ptr::read`] is guaranteed to be safe.
335        /// If you do not read from it, your consumer will forgotten without running its
336        /// destructor, as with [`mem::forget`].
337        ///
338        /// [issue #20041]: https://github.com/rust-lang/rust/issues/20041
339        /// [`ptr::read`]: core::ptr::read
340        /// [`mem::forget`]: core::mem::forget
341        fn consume_eq(&self) -> Self::Result
342        where
343            T: IsEqual<U>;
344    }
345
346    /// Trait used to convince the rust type checker of the claimed equality.
347    ///
348    /// If your consumer takes a generic parameter `T`, store values with type
349    /// `<T as AliasSelf>::Alias` instead of `T` directly. In `consume_eq`, the compiler
350    /// will correctly reduce this to `U`, since it sees the `where` clause. Additionally,
351    /// during construction (somewhere else), the compiler sees the `impl<T> AssocSelf for T`,
352    /// correctly using the first equality. Thus, you shouldn't have to coerce consumers.
353    ///
354    pub trait AliasSelf {
355        /// Always set to `Self`, but the type checker doesn't reduce `T::Alias` to `T`.
356        type Alias: ?Sized;
357    }
358    impl<T: ?Sized> AliasSelf for T {
359        type Alias = T;
360    }
361}
362
363/// [`TypeFunction`]s have the amazing property that they can be used to push the equality of a
364/// type-level argument through to an equality of the type-level result.
365///
366/// In this crate, helper structs are defined that implement `TypeFunction` with various resulting
367/// types. These structs are then supposed to be passed to [`substitute`], [`TypeEq::substitute`]
368/// and [`TypeEq::lift_through`].
369///
370/// # Example
371///
372/// ```
373/// # use type_equalities::{refl, TypeEq};
374/// # use type_equalities::type_functions::RefF;
375/// let eq: TypeEq<&u32, &u32> = refl::<u32>().lift_through::<RefF<'_>>();
376/// ```
377///
378pub mod type_functions {
379    use super::*;
380    #[cfg(feature = "alloc")]
381    use alloc::boxed::Box;
382
383    /// A trait for type level functions, mapping type arguments to type results.
384    ///
385    /// Note that `Self` is used only as a marker. See also [`substitute`], which implements coercing of results.
386    pub trait TypeFunction<Arg: ?Sized> {
387        /// The type that `Arg` is mapped to by the implementor.
388        type Result: ?Sized;
389    }
390    /// The result of applying the [`TypeFunction`] `F` to `T`.
391    pub type ApF<F, T> = <F as TypeFunction<T>>::Result;
392
393    /// The identity [`TypeFunction`], `ApF<IdF, T> == T`. Coercing through this gives
394    /// us the basic safe transmute.
395    pub struct IdF;
396    impl<A: ?Sized> TypeFunction<A> for IdF {
397        type Result = A;
398    }
399    /// The [`TypeFunction`] `ApF<BoxF, A> == Box<A>`
400    #[cfg(feature = "alloc")]
401    #[rustversion::attr(nightly, doc(cfg(feature = "alloc")))]
402    pub struct BoxF;
403    #[cfg(feature = "alloc")]
404    impl<A: ?Sized> TypeFunction<A> for BoxF {
405        type Result = Box<A>;
406    }
407    /// The [`TypeFunction`] `ApF<RefF<'a>, A> == &'a A`
408    pub struct RefF<'a>(PhantomData<&'a ()>);
409    impl<'a, A: ?Sized + 'a> TypeFunction<A> for RefF<'a> {
410        type Result = &'a A;
411    }
412
413    /// The [`TypeFunction`] `ApF<MutRefF<'a>, A> == &'a mut A`
414    pub struct MutRefF<'a>(PhantomData<&'a ()>);
415    impl<'a, A: ?Sized + 'a> TypeFunction<A> for MutRefF<'a> {
416        type Result = &'a mut A;
417    }
418
419    /// The [`TypeFunction`] `ApF<SliceF<N>, A> == [A; N]`
420    pub struct SliceF<const N: usize>(PhantomData<*const [(); N]>);
421    impl<A, const N: usize> TypeFunction<A> for SliceF<N> {
422        type Result = [A; N];
423    }
424
425    /// A [`TypeFunction`] version of the Martin-Löf identity type:
426    /// `ApF<LoefIdF<T>, U> == TypeEq<T, U>`.
427    pub struct LoefIdF<T: ?Sized>(PhantomData<T>);
428    impl<T: ?Sized, Arg: ?Sized> TypeFunction<Arg> for LoefIdF<T> {
429        type Result = TypeEq<T, Arg>;
430    }
431    /// [`LoefIdF`] flipped, i.e. `ApF<LoefIdFlippedF<T>, U> == TypeEq<U, T>`
432    pub struct LoefIdFlippedF<T: ?Sized>(PhantomData<T>);
433    impl<T: ?Sized, Arg: ?Sized> TypeFunction<Arg> for LoefIdFlippedF<T> {
434        type Result = TypeEq<Arg, T>;
435    }
436
437    /// Composition for [`TypeFunction`]s, i.e. `ApF<ComposeF<F, G>, T> == ApF<F, ApF<G, T>>`
438    pub struct ComposeF<F: ?Sized, G: ?Sized>(PhantomData<F>, PhantomData<G>);
439    impl<F: ?Sized, G: ?Sized, Arg: ?Sized> TypeFunction<Arg> for ComposeF<F, G>
440    where
441        G: TypeFunction<Arg>,
442        F: TypeFunction<G::Result>,
443    {
444        type Result = F::Result;
445    }
446}
447
448mod kernel {
449    use crate::details::Consumer;
450    use core::{marker::PhantomData, mem::ManuallyDrop, ops::Deref};
451
452    pub(crate) struct TheEq<T: ?Sized, U: ?Sized> {
453        _phantomt: PhantomData<*const core::cell::Cell<T>>,
454        _phantomu: PhantomData<*const core::cell::Cell<U>>,
455    }
456
457    impl<T: ?Sized, U: ?Sized> Clone for TheEq<T, U> {
458        fn clone(&self) -> Self {
459            *self
460        }
461    }
462    impl<T: ?Sized, U: ?Sized> Copy for TheEq<T, U> {}
463
464    pub(crate) const fn refl<T: ?Sized>() -> TheEq<T, T> {
465        // This is the only place where a TypeEq is constructed
466        TheEq {
467            _phantomt: PhantomData,
468            _phantomu: PhantomData,
469        }
470    }
471
472    pub(crate) fn use_eq<T: ?Sized, U: ?Sized, C: Consumer<T, U>>(
473        _: TheEq<T, U>,
474        c: C,
475    ) -> C::Result {
476        // By our invariant of only constructing `TheEq<T, T>`, we know here that `U = T`.
477        // Use this to transmute the consumer
478        let the_c = ManuallyDrop::new(c);
479        let ref_c: &dyn Consumer<T, U, Result = C::Result> = the_c.deref();
480        let tref_c: &dyn Consumer<T, T, Result = C::Result> =
481            unsafe { core::mem::transmute(ref_c) };
482        tref_c.consume_eq()
483    }
484}
485
486/// Optionally obtain a type equality if the type checker can solve `T == U`.
487///
488/// Note that this depends on `#![feature(specialization)]` and works by overloading
489/// some defined instances. Do not depend on always getting back a `Some(..)`, but
490/// it will work fine in the simple cases.
491///
492/// # Examples
493///
494/// ```
495/// # use type_equalities::maybe_type_eq;
496/// # fn run() -> Option<()> {
497/// assert_eq!(maybe_type_eq::<u32, u32>()?.coerce(42), 42);
498/// # Some(()) }
499/// # run().ok_or("failed to prove equality")?;
500/// # Ok::<(), &'static str>(())
501/// ```
502#[cfg(feature = "test-for-type-equality")]
503#[rustversion::attr(nightly, doc(cfg(feature = "test-for-type-equality")))]
504pub const fn maybe_type_eq<T: ?Sized, U: ?Sized>() -> Option<TypeEq<T, U>> {
505    // Helper trait. `VALUE` is false, except for the specialization of the
506    // case where `T == U`.
507    trait ObsTypeEq<U: ?Sized> {
508        const VALUE: Option<TypeEq<Self, U>>;
509    }
510
511    // Default implementation.
512    impl<T: ?Sized, U: ?Sized> ObsTypeEq<U> for T {
513        default const VALUE: Option<TypeEq<T, U>> = None;
514    }
515    // Specialization for `T == U`.
516    impl<T: ?Sized> ObsTypeEq<T> for T {
517        const VALUE: Option<TypeEq<T, T>> = Some(refl::<T>());
518    }
519
520    <T as ObsTypeEq<U>>::VALUE
521}
522
523#[cfg(all(test, feature = "test-for-type-equality"))]
524mod test {
525    use crate::*;
526
527    fn test_type_eq<T, U>(t: T) -> Option<U> {
528        match maybe_type_eq() {
529            None => None,
530            Some(eq) => Some(eq.coerce(t)),
531        }
532    }
533
534    #[test]
535    fn test_some_integers() {
536        assert_eq!(test_type_eq::<i32, i32>(0), Some(0));
537        assert_eq!(test_type_eq::<&i32, &i32>(&0).copied(), Some(0));
538        assert_eq!(test_type_eq::<&i32, i32>(&0), None);
539        assert_eq!(test_type_eq::<i32, u32>(0), None);
540    }
541}