use crate::{
type_fn::{self, CallFn, InvokeAlias, TypeFn},
MakeTypeWitness, TypeWitnessTypeArg,
};
#[cfg(feature = "const_marker")]
use crate::const_marker::Usize;
use core::{
cmp::{Ordering, Eq, Ord, PartialEq, PartialOrd},
default::Default,
hash::{Hash, Hasher},
fmt::{self, Debug},
};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
macro_rules! projected_type_eq {
($type_eq:expr, $L:ty, $R:ty, $F:ty) => ({
unsafe {
$crate::type_eq::__ProjectVars::<$F, $L, $R> {
te: $type_eq,
projected_te: $crate::TypeEq::new_unchecked(),
}.projected_te
}
})
}
struct __ProjectVars<F, L: ?Sized, R: ?Sized>
where
InvokeAlias<F>: TypeFn<L> + TypeFn<R>
{
#[allow(dead_code)]
te: TypeEq<L, R>,
projected_te: TypeEq<CallFn<InvokeAlias<F>, L>, CallFn<InvokeAlias<F>, R>>,
}
macro_rules! __zip_impl {
($( $type_eq:ident [$L:ident, $R:ident] ),* $(,)*) => {
$(
let _te: $crate::TypeEq<$L, $R> = $type_eq;
)*
unsafe {
$crate::TypeEq::<($($L,)*), ($($R,)*)>::new_unchecked()
}
}
}
macro_rules! zip_project {
(
$left_type_eq:expr,
$right_type_eq:expr,
$F: ty,
($L0:ty, $R0:ty),
($L1:ty, $R1:ty),
) => ({
$crate::type_eq::__ZipProjectVars::<$F, $L0, $R0, $L1, $R1> {
left_te: $left_type_eq,
right_te: $right_type_eq,
projected_te: {
// SAFETY:
// `$L0 == $R0` and `$L1 == $R1` implies `($L0, $L1) == ($R0, $R1)`,
unsafe {
$crate::TypeEq::new_unchecked()
}
}
}.projected_te
});
}
struct __ZipProjectVars<F, L0, R0, L1, R1>
where
F: TypeFn<(L0, L1)> + TypeFn<(R0, R1)>
{
#[allow(dead_code)]
left_te: TypeEq<L0, R0>,
#[allow(dead_code)]
right_te: TypeEq<L1, R1>,
projected_te: TypeEq<CallFn<F, (L0, L1)>, CallFn<F, (R0, R1)>>,
}
#[doc(hidden)]
pub type __ProjectedTypeEq<F, L, R> = TypeEq<CallFn<F, L>, CallFn<F, R>>;
#[inline(always)]
pub const fn type_eq<T: ?Sized>() -> TypeEq<T, T> {
TypeEq::NEW
}
mod type_eq_ {
use core::marker::PhantomData;
pub struct TypeEq<L: ?Sized, R: ?Sized>(PhantomData<TypeEqHelper<L, R>>);
struct TypeEqHelper<L: ?Sized, R: ?Sized>(
fn(PhantomData<L>) -> PhantomData<L>,
fn(PhantomData<R>) -> PhantomData<R>,
);
impl<T: ?Sized> TypeEq<T, T> {
pub const NEW: Self = TypeEq(PhantomData);
}
impl TypeEq<(), ()> {
#[inline(always)]
pub const fn new<T: ?Sized>() -> TypeEq<T, T> {
TypeEq::<T, T>::NEW
}
}
impl<L: ?Sized, R: ?Sized> TypeEq<L, R> {
#[inline(always)]
pub const unsafe fn new_unchecked() -> TypeEq<L, R> {
TypeEq(PhantomData)
}
#[inline(always)]
pub const fn flip(self: TypeEq<L, R>) -> TypeEq<R, L> {
TypeEq(PhantomData)
}
#[inline(always)]
pub const fn join<O: ?Sized>(self: TypeEq<L, R>, _other: TypeEq<R, O>) -> TypeEq<L, O> {
TypeEq(PhantomData)
}
}
}
pub use type_eq_::TypeEq;
impl<L: ?Sized, R: ?Sized> Copy for TypeEq<L, R> {}
impl<L: ?Sized, R: ?Sized> Clone for TypeEq<L, R> {
fn clone(&self) -> Self {
*self
}
}
impl<L0, R0> TypeEq<L0, R0> {
#[cfg_attr(not(feature = "const_marker"), doc = "```ignore")]
#[cfg_attr(feature = "const_marker", doc = "```rust")]
#[inline(always)]
pub const fn zip<L1, R1>(
self: TypeEq<L0, R0>,
other: TypeEq<L1, R1>,
) -> TypeEq<(L0, L1), (R0, R1)> {
__zip_impl!{self[L0, R0], other[L1, R1]}
}
pub const fn zip3<L1, R1, L2, R2>(
self: TypeEq<L0, R0>,
other1: TypeEq<L1, R1>,
other2: TypeEq<L2, R2>,
) -> TypeEq<(L0, L1, L2), (R0, R1, R2)> {
__zip_impl!{
self[L0, R0],
other1[L1, R1],
other2[L2, R2],
}
}
pub const fn zip4<L1, R1, L2, R2, L3, R3>(
self: TypeEq<L0, R0>,
other1: TypeEq<L1, R1>,
other2: TypeEq<L2, R2>,
other3: TypeEq<L3, R3>,
) -> TypeEq<(L0, L1, L2, L3), (R0, R1, R2, R3)> {
__zip_impl!{
self[L0, R0],
other1[L1, R1],
other2[L2, R2],
other3[L3, R3],
}
}
}
impl<L, R> TypeEq<L, R> {
const ARE_SAME_TYPE: Amb = {
let approx_same_type = {
core::mem::size_of::<L>() == core::mem::size_of::<R>()
&& core::mem::align_of::<L>() == core::mem::align_of::<R>()
&& core::mem::size_of::<Option<L>>() == core::mem::size_of::<Option<R>>()
&& core::mem::align_of::<Option<L>>() == core::mem::align_of::<Option<R>>()
};
if approx_same_type {
Amb::Indefinite
} else {
Amb::No
}
};
#[inline(always)]
pub const fn reachability_hint<T>(self, val: T) -> T {
if let Amb::No = Self::ARE_SAME_TYPE {
unsafe { core::hint::unreachable_unchecked() }
}
val
}
#[inline(always)]
pub const fn to_right(self, from: L) -> R {
self.reachability_hint(());
unsafe { crate::__priv_transmute!(L, R, from) }
}
#[inline(always)]
pub const fn to_left(self, from: R) -> L {
self.reachability_hint(());
unsafe { crate::__priv_transmute!(R, L, from) }
}
}
impl<L: ?Sized, R: ?Sized> TypeWitnessTypeArg for TypeEq<L, R> {
type Arg = L;
}
impl<T: ?Sized> MakeTypeWitness for TypeEq<T, T> {
const MAKE: Self = Self::NEW;
}
impl<L: ?Sized, R: ?Sized> TypeEq<L, R> {
pub const fn map<F>(
self,
func: F,
) -> TypeEq<CallFn<InvokeAlias<F>, L>, CallFn<InvokeAlias<F>, R>>
where
InvokeAlias<F>: crate::TypeFn<L> + crate::TypeFn<R>
{
core::mem::forget(func);
projected_type_eq!{self, L, R, InvokeAlias<F>}
}
pub const fn project<F>(self) -> TypeEq<CallFn<InvokeAlias<F>, L>, CallFn<InvokeAlias<F>, R>>
where
InvokeAlias<F>: crate::TypeFn<L> + crate::TypeFn<R>
{
projected_type_eq!{self, L, R, InvokeAlias<F>}
}
pub const fn in_ref<'a>(self) -> TypeEq<&'a L, &'a R> {
projected_type_eq!{self, L, R, type_fn::GRef<'a>}
}
crate::utils::conditionally_const!{
feature = "mut_refs";
#[cfg_attr(not(feature = "mut_refs"), doc = "```ignore")]
#[cfg_attr(feature = "mut_refs", doc = "```rust")]
#[cfg_attr(feature = "nightly_mut_refs", doc = "# #![feature(const_mut_refs)]")]
pub fn in_mut['a](self) -> TypeEq<&'a mut L, &'a mut R> {
projected_type_eq!{self, L, R, type_fn::GRefMut<'a>}
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
pub const fn in_box(self) -> TypeEq<Box<L>, Box<R>> {
projected_type_eq!{self, L, R, type_fn::GBox}
}
}
#[cfg(feature = "const_marker")]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "const_marker")))]
impl<L: Sized, R: Sized> TypeEq<L, R> {
#[cfg_attr(not(feature = "rust_1_61"), doc = "```ignore")]
#[cfg_attr(feature = "rust_1_61", doc = "```rust")]
#[inline(always)]
pub const fn in_array<const UL: usize, const UR: usize>(
self,
other: TypeEq<Usize<UL>, Usize<UR>>,
) -> TypeEq<[L; UL], [R; UR]> {
struct PairToArray;
impl<T, const N: usize> TypeFn<(T, Usize<N>)> for PairToArray {
type Output = [T; N];
}
zip_project!{
self,
other,
PairToArray,
(L, R),
(Usize<UL>, Usize<UR>),
}
}
}
enum Amb {
Indefinite,
No,
}
impl<T: ?Sized> Default for TypeEq<T, T> {
fn default() -> Self {
Self::NEW
}
}
impl<L: ?Sized, R: ?Sized> Debug for TypeEq<L, R> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("TypeEq")
}
}
impl<L: ?Sized, R: ?Sized> PartialEq for TypeEq<L, R> {
fn eq(&self, _: &Self) -> bool {
true
}
}
impl<L: ?Sized, R: ?Sized> PartialOrd for TypeEq<L, R> {
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
Some(Ordering::Equal)
}
}
impl<L: ?Sized, R: ?Sized> Ord for TypeEq<L, R> {
fn cmp(&self, _: &Self) -> Ordering {
Ordering::Equal
}
}
impl<L: ?Sized, R: ?Sized> Eq for TypeEq<L, R> {}
impl<L: ?Sized, R: ?Sized> Hash for TypeEq<L, R> {
fn hash<H>(&self, _state: &mut H)
where H: Hasher
{}
}