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}