You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
reverse/src/ext.rs

163 lines
5.0 KiB

//! Extension traits, global functions + macros.
#![allow(unused)]
use super::*;
use std::convert::Infallible;
/// The default bottom type.
///
/// To use the `unwrap_infallible()`-like interface, functions that return `-> !` should be changed to `-> Never`.
/// When `unstable` is enabled, this is an alias to `!` and `-> !` is not special cased.
///
/// # As return argument
/// When feature `unstable` is enabled, `into_unreachable()` may not be required to ensure propogation to `!` from a function returning `-> Never`.
#[cfg(feature="unstable")]
pub type Never = !;
/// The default bottom type.
///
/// To use the `unwrap_infallible()`-like interface, functions special cased to `-> !` should be changed to `-> Never`.
///
/// # As return argument
/// When feature `unstable` is not enabled, `into_unreachable()` may be required to be used when dealing with return bottom types other than the special case `-> !`.
/// This is a current limitation of the type system.
#[cfg(not(feature="unstable"))]
pub type Never = Infallible;
/// Contractually ensures this type cannot exist (i.e. it is a bottom type.)
///
/// # Safety
/// Instances of the impl type **cannot exist**.
/// They must be bottom types (i.e. empty enums, types contatining an `Infallible` / `!` object, etc.)
///
/// # Auto-impl
/// This trait is not intended to be implemented on any user-defined type other than empty enums.
///
/// By default it is implemented for the following types:
/// - `core::convert::Infallible`
/// - `!` (**feature**: `unstable`)
/// - `Box<T>` *where* `T: ?Sized + Unreachable`
pub unsafe trait Unreachable {
/// Force control flow to terminate type checking here.
///
/// # Note
/// This function will never be executed, it is used to terminate the value's existence in the type system, by converting it from any `Unreachable` type into the bottom return type `!`.
/// If this function ever **can** be called at all, it is undefined behaviour.
#[inline]
#[cold]
fn into_unreachable(self) -> !
where Self: Sized
{
if cfg!(debug_assertions) {
unreachable!("Unreachable conversion from {}!", std::any::type_name::<Self>())
} else {
// SAFETY: Contractually enforced by the trait impl itself.
unsafe {
std::hint::unreachable_unchecked()
}
}
}
}
unsafe impl Unreachable for Infallible {
#[inline(always)]
#[cold]
fn into_unreachable(self) -> ! {
match self {}
}
}
#[cfg(feature="unstable")]
unsafe impl Unreachable for ! {
#[inline(always)]
#[cold]
fn into_unreachable(self) -> ! {
match self {}
}
}
unsafe impl<T: ?Sized + Unreachable> Unreachable for Box<T> {}
pub trait UnwrapPanicExt<T, E> {
/// Unwrap the result `Ok` value or panic as described by the non-returning function `F`, with the `Unreachable` bottom type `N`.
/// This will usually be an `-> !` function, (or an `-> Never` function using the `Unreachable` interface.)
///
/// # Panic usage
/// `func` must not return. It should panic, resume a panic, or exit the thread/program, trap, or terminate in an infinite loop.
///
/// It does not *have* to call `panic!()` if it terminates in another way that is not a panic, however.
fn unwrap_or_panic<N: Unreachable, F: FnOnce(E) -> N>(self, func: F) -> T;
/// Unwrap the result `Ok` value or panic as described by the non-returning function `func` with the default bottom type `Never`.
#[inline(always)]
fn unwrap_or_panic_unreachable<F: FnOnce(E) -> Never>(self, func: F) -> T
where Self: Sized {
self.unwrap_or_panic::<Never, _>(func)
}
}
pub trait UnwrapInfallibleExt<T> {
/// Unwrapping is infallible and therefore safe to do so without checking.
fn unwrap_infallible(self) -> T;
}
pub trait UnwrapPanicResumeExt<T> {
/// Unwrap or resume a previous unwind, with the unwind payload in the `Err` variant.
fn unwrap_or_resume(self) -> T;
}
impl<T, E> UnwrapPanicExt<T, E> for Result<T, E>
{
#[inline]
fn unwrap_or_panic<N: Unreachable, F: FnOnce(E) -> N>(self, func: F) -> T {
#[inline(never)]
#[cold]
fn _do_panic<Nn: Unreachable, Ee, Ff: FnOnce(Ee) -> Nn>(error: Ee, func: Ff) -> !
{
func(error).into_unreachable()
}
match self {
Ok(v) => v,
Err(e) => _do_panic(e, func)
}
}
}
impl<T, E: Unreachable> UnwrapInfallibleExt<T> for Result<T, E>
{
#[inline]
fn unwrap_infallible(self) -> T {
match self {
Ok(v) => v,
Err(e) => if cfg!(debug_assertions) {
e.into_unreachable()
} else {
// SAFETY: Contract bound of `E: Unreachable` ensures this path will never be taken.
unsafe {
std::hint::unreachable_unchecked()
}
}
}
}
}
/// The type of a caught unwind payload.
pub type UnwindPayload = Box<dyn std::any::Any + Send>;
#[cold]
#[inline(never)]
fn _resume_unwind<E: Into<UnwindPayload>>(e: E) -> !
{
std::panic::resume_unwind(e.into())
}
impl<T, E: Into<UnwindPayload>> UnwrapPanicResumeExt<T> for Result<T, E>
{
#[inline]
fn unwrap_or_resume(self) -> T {
match self {
Ok(v) => v,
Err(e) => _resume_unwind(e),
}
}
}