//! Helper module for exporting the `pattern_type` macro

use crate::marker::{Freeze, PointeeSized, Unsize};
use crate::ops::{CoerceUnsized, DispatchFromDyn};

/// Creates a pattern type.
/// ```ignore (cannot test this from within core yet)
/// type Positive = std::pat::pattern_type!(i32 is 1..);
/// ```
#[macro_export]
#[rustc_builtin_macro(pattern_type)]
#[unstable(feature = "pattern_type_macro", issue = "123646")]
macro_rules! pattern_type {
    ($($arg:tt)*) => {
        /* compiler built-in */
    };
}

/// A trait implemented for integer types and `char`.
/// Useful in the future for generic pattern types, but
/// used right now to simplify ast lowering of pattern type ranges.
#[unstable(feature = "pattern_type_range_trait", issue = "123646")]
#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
#[diagnostic::on_unimplemented(
    message = "`{Self}` is not a valid base type for range patterns",
    label = "only integer types and `char` are supported"
)]
pub const trait RangePattern {
    /// Trait version of the inherent `MIN` assoc const.
    #[lang = "RangeMin"]
    const MIN: Self;

    /// Trait version of the inherent `MIN` assoc const.
    #[lang = "RangeMax"]
    const MAX: Self;

    /// A compile-time helper to subtract 1 for exclusive ranges.
    #[lang = "RangeSub"]
    #[track_caller]
    fn sub_one(self) -> Self;
}

macro_rules! impl_range_pat {
    ($($ty:ty,)*) => {
        $(
            #[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
            impl const RangePattern for $ty {
                const MIN: $ty = <$ty>::MIN;
                const MAX: $ty = <$ty>::MAX;
                fn sub_one(self) -> Self {
                    match self.checked_sub(1) {
                        Some(val) => val,
                        None => panic!("exclusive range end at minimum value of type")
                    }
                }
            }
        )*
    }
}

impl_range_pat! {
    i8, i16, i32, i64, i128, isize,
    u8, u16, u32, u64, u128, usize,
}

#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
impl const RangePattern for char {
    const MIN: Self = char::MIN;

    const MAX: Self = char::MAX;

    fn sub_one(self) -> Self {
        match char::from_u32(self as u32 - 1) {
            None => panic!("exclusive range to start of valid chars"),
            Some(val) => val,
        }
    }
}

impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<pattern_type!(*const U is !null)> for pattern_type!(*const T is !null) where
    T: Unsize<U>
{
}

impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<pattern_type!(U is !null)> for pattern_type!(T is !null) {}

impl<T: PointeeSized> Unpin for pattern_type!(*const T is !null) {}

unsafe impl<T: PointeeSized> Freeze for pattern_type!(*const T is !null) {}

unsafe impl<T: PointeeSized> Freeze for pattern_type!(*mut T is !null) {}
