// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Provide implementation and test of the `ForLt` trait and macro.
//!
//! This module is hidden and user should just use `ForLt!` directly.

use core::marker::PhantomData;

/// Representation of types generic over a lifetime.
///
/// The type must be covariant over the generic lifetime, i.e. the lifetime parameter
/// can be soundly shortened.
///
/// The lifetime involved must be covariant.
///
/// # Macro
///
/// It is not recommended to implement this trait directly. `ForLt!` macro is provided to obtain a
/// type that implements this trait.
///
/// The full syntax is
///
/// ```
/// # use kernel::types::ForLt;
/// # fn expect_lt<F: ForLt>() {}
/// # struct TypeThatUse<'a>(&'a ());
/// # expect_lt::<
/// ForLt!(for<'a> TypeThatUse<'a>)
/// # >();
/// ```
///
/// which gives a type so that `<ForLt!(for<'a> TypeThatUse<'a>) as ForLt>::Of<'b>`
/// is `TypeThatUse<'b>`.
///
/// You may also use a short-hand syntax which works similar to lifetime elision.
/// The macro also accepts types that do not involve a lifetime at all.
///
/// ```
/// # use kernel::types::ForLt;
/// # fn expect_lt<F: ForLt>() {}
/// # struct TypeThatUse<'a>(&'a ());
/// # expect_lt::<
/// ForLt!(TypeThatUse<'_>) // Equivalent to `ForLt!(for<'a> TypeThatUse<'a>)`.
/// # >();
/// # expect_lt::<
/// ForLt!(&u32) // Equivalent to `ForLt!(for<'a> &'a u32)`.
/// # >();
/// # expect_lt::<
/// ForLt!(u32) // Equivalent to `ForLt!(for<'a> u32)`.
/// # >();
/// ```
///
/// The macro will attempt to prove that the type is indeed covariant over the lifetime supplied.
/// When it cannot be syntactically proven, it will emit checks to ask the Rust compiler to prove
/// it.
///
/// ```ignore,compile_fail
/// # use kernel::types::ForLt;
/// # fn expect_lt<F: ForLt>() {}
/// # expect_lt::<
/// ForLt!(fn(&u32)) // Contravariant, will fail compilation.
/// # >();
/// ```
///
/// There is a limitation if the type refers to generic parameters; if the macro cannot prove the
/// covariance syntactically, the emitted checks will fail the compilation as it needs to refer to
/// the generic parameter but is in a separate item.
///
/// ```
/// # use kernel::types::ForLt;
/// fn expect_lt<F: ForLt>() {}
/// # #[allow(clippy::unnecessary_safety_comment, reason = "false positive")]
/// fn generic_fn<T: 'static>() {
///     // Syntactically proven by the macro
///     expect_lt::<ForLt!(&T)>();
///     // Syntactically proven by the macro
///     expect_lt::<ForLt!(&KBox<T>)>();
///     // Cannot be syntactically proven, need to check covariance of `KBox`
///     // expect_lt::<ForLt!(&KBox<&T>)>();
/// }
/// ```
///
/// # Safety
///
/// `Self::Of<'a>` must be covariant over the lifetime `'a`.
pub unsafe trait ForLt {
    /// The type parameterized by the lifetime.
    type Of<'a>: 'a;

    /// Cast a reference to a shorter lifetime.
    #[inline(always)]
    fn cast_ref<'r, 'short: 'r, 'long: 'short>(long: &'r Self::Of<'long>) -> &'r Self::Of<'short> {
        // SAFETY: This is sound as this trait guarantees covariance.
        unsafe { core::mem::transmute(long) }
    }
}
pub use macros::ForLt;

/// This is intended to be an "unsafe-to-refer-to" type.
///
/// Must only be used by the `ForLt!` macro.
///
/// `T` is the magic `dyn for<'a> WithLt<'a, TypeThatUse<'a>>` generated by macro.
///
/// `WF` is a type that the macro can use to assert some specific type is well-formed.
///
/// `N` is to provide the macro a place to emit arbitrary items, in case it needs to prove
/// additional properties.
#[doc(hidden)]
pub struct UnsafeForLtImpl<T: ?Sized, WF, const N: usize>(PhantomData<(WF, T)>);

// This is a helper trait for implementation `ForLt` to be able to use HRTB.
#[doc(hidden)]
pub trait WithLt<'a> {
    type Of: 'a;
}

// SAFETY: In `ForLt!` macro, a covariance proof is generated when naming `UnsafeForLtImpl`
// and it will fail to evaluate if the type is not covariant.
unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> ForLt for UnsafeForLtImpl<T, WF, 0> {
    type Of<'a> = <T as WithLt<'a>>::Of;
}
