summaryrefslogtreecommitdiff
path: root/rust/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'rust/kernel')
-rw-r--r--rust/kernel/auxiliary.rs78
-rw-r--r--rust/kernel/devres.rs106
-rw-r--r--rust/kernel/io/mem.rs65
-rw-r--r--rust/kernel/pci.rs1
-rw-r--r--rust/kernel/pci/io.rs37
-rw-r--r--rust/kernel/types.rs5
-rw-r--r--rust/kernel/types/for_lt.rs103
7 files changed, 317 insertions, 78 deletions
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index c42928d5a239..19a488700bb9 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -20,6 +20,7 @@ use crate::{
},
prelude::*,
types::{
+ CovariantForLt,
ForLt,
ForeignOwnable,
Opaque, //
@@ -270,18 +271,15 @@ impl Device<device::Bound> {
unsafe { parent.as_bound() }
}
- /// Returns a pinned reference to the registration data set by the registering (parent) driver.
+ /// Returns the stored registration data as a pinned reference.
///
- /// `F` is the [`ForLt`](trait@ForLt) encoding of the data type. The returned
- /// reference has its lifetime shortened from `'static` to `&self`'s borrow lifetime via
- /// [`ForLt::cast_ref`].
+ /// Performs null and [`TypeId`] checks, then borrows the stored [`KBox`].
///
- /// Returns [`EINVAL`] if `F` does not match the type used by the parent driver when calling
- /// [`Registration::new()`].
+ /// # Safety
///
- /// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was
- /// registered by a C driver.
- pub fn registration_data<F: ForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
+ /// Callers must ensure that the lifetime shortening from the original `'static` storage to
+ /// `'_` is sound, e.g. via an HRTB closure or [`CovariantForLt`] guarantee.
+ unsafe fn registration_data_pinned<F: ForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
// SAFETY: By the type invariant, `self.as_raw()` is a valid `struct auxiliary_device`.
let ptr = unsafe { (*self.as_raw()).registration_data_rust };
if ptr.is_null() {
@@ -300,17 +298,59 @@ impl Device<device::Bound> {
return Err(EINVAL);
}
- // SAFETY: The `TypeId` check above confirms that the stored type matches
- // `F::Of<'static>`; `ptr` remains valid until `Registration::drop()` calls
- // `from_foreign()`.
- let wrapper = unsafe { Pin::<KBox<RegistrationData<F::Of<'static>>>>::borrow(ptr) };
+ // SAFETY: The `TypeId` check above confirms that the stored type matches `F`'s
+ // encoding; lifetimes are erased at runtime, so borrowing as `F::Of<'_>` is
+ // layout-compatible with the stored `F::Of<'static>`. `ptr` remains valid until
+ // `Registration::drop()` calls `from_foreign()`.
+ let wrapper = unsafe { Pin::<KBox<RegistrationData<F::Of<'_>>>>::borrow(ptr) };
// SAFETY: `data` is a structurally pinned field of `RegistrationData`.
- let pinned: Pin<&F::Of<'_>> = unsafe { wrapper.map_unchecked(|w| &w.data) };
+ Ok(unsafe { wrapper.map_unchecked(|w| &w.data) })
+ }
- // SAFETY: The data was pinned when stored; `cast_ref` only shortens
- // the lifetime, so the pinning guarantee is preserved.
- Ok(unsafe { Pin::new_unchecked(F::cast_ref(pinned.get_ref())) })
+ /// Access the registration data set by the registering (parent) driver through a closure.
+ ///
+ /// `F` is the [`ForLt`](trait@ForLt) encoding of the data type. The closure receives a pinned
+ /// reference to the registration data.
+ ///
+ /// For covariant types that implement [`trait@CovariantForLt`], prefer
+ /// [`registration_data`](Self::registration_data) which returns a direct reference.
+ ///
+ /// Returns [`EINVAL`] if `F` does not match the type used by the parent driver when calling
+ /// [`Registration::new()`].
+ ///
+ /// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was
+ /// registered by a C driver.
+ #[inline]
+ pub fn registration_data_with<F: ForLt + 'static, R>(
+ &self,
+ f: impl for<'a> FnOnce(Pin<&'a F::Of<'a>>) -> R,
+ ) -> Result<R> {
+ // SAFETY: The HRTB closure prevents the caller from smuggling in references with a
+ // concrete short lifetime, making the round-trip from `'static` sound regardless of
+ // variance.
+ let pinned = unsafe { self.registration_data_pinned::<F>()? };
+
+ Ok(f(pinned))
+ }
+
+ /// Returns a pinned reference to the registration data set by the registering (parent) driver.
+ ///
+ /// This method is only available when `F` implements [`trait@CovariantForLt`], which guarantees
+ /// that the lifetime shortening is sound.
+ ///
+ /// For non-covariant types, use the closure-based [`Self::registration_data_with`].
+ ///
+ /// Returns [`EINVAL`] if `F` does not match the type used by the parent driver when calling
+ /// [`Registration::new()`].
+ ///
+ /// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was
+ /// registered by a C driver.
+ #[inline]
+ pub fn registration_data<F: CovariantForLt + 'static>(&self) -> Result<Pin<&F::Of<'_>>> {
+ // SAFETY: `CovariantForLt` guarantees covariance, which makes the lifetime shortening
+ // from `'static` to `'_` performed by `registration_data_pinned` sound.
+ unsafe { self.registration_data_pinned::<F>() }
}
}
@@ -401,7 +441,9 @@ struct RegistrationData<T> {
///
/// The type parameter `F` is a [`ForLt`](trait@ForLt) encoding of the registration
/// data type. For non-lifetime-parameterized types, use [`ForLt!(T)`](macro@ForLt).
-/// The data can be accessed by the auxiliary driver through [`Device::registration_data()`].
+///
+/// The data can be accessed by the auxiliary driver through [`Device::registration_data()`] and
+/// [`Device::registration_data_with()`].
///
/// # Invariants
///
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 11ce500e9b76..b7c075a39ba4 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -24,6 +24,8 @@ use crate::{
Arc, //
},
types::{
+ CovariantForLt,
+ ForLt,
ForeignOwnable,
Opaque, //
},
@@ -365,6 +367,110 @@ impl<T: Send + 'static> Drop for Devres<T> {
}
}
+/// Guard returned by [`DevresLt::try_access`].
+///
+/// Dereferences to `F::Of<'a>`, shortening the lifetime of the stored data to the guard's borrow
+/// lifetime.
+pub struct DevresGuard<'a, F: CovariantForLt>(RevocableGuard<'a, F::Of<'static>>);
+
+impl<'a, F: CovariantForLt> core::ops::Deref for DevresGuard<'a, F> {
+ type Target = F::Of<'a>;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ F::cast_ref(&*self.0)
+ }
+}
+
+/// Device-managed resource with [`ForLt`](trait@ForLt)-aware access.
+///
+/// `DevresLt` wraps [`Devres`] and shortens the stored `'static` lifetime to the caller's borrow
+/// lifetime in all access methods.
+///
+/// Types that implement [`trait@CovariantForLt`] get direct-reference accessors ([`Self::access`],
+/// [`Self::try_access`]). Plain [`ForLt`](trait@ForLt) types use closure-based accessors
+/// ([`Self::access_with`], [`Self::try_access_with`]).
+pub struct DevresLt<F: ForLt>(Devres<F::Of<'static>>)
+where
+ for<'a> F::Of<'a>: Send;
+
+impl<F: ForLt> DevresLt<F>
+where
+ for<'a> F::Of<'a>: Send,
+{
+ /// Creates a new [`DevresLt`] instance of the given `data`.
+ ///
+ /// # Safety
+ ///
+ /// The data must remain valid for the device's full bound scope. [`DevresLt`] allows
+ /// access until the device is unbound, which may outlast `'a`.
+ pub unsafe fn new<'a, E>(
+ dev: &'a Device<Bound>,
+ data: impl PinInit<F::Of<'a>, E>,
+ ) -> Result<Self>
+ where
+ Error: From<E>,
+ {
+ // SAFETY: The caller guarantees the data is valid for the device's full bound scope.
+ // Lifetimes do not affect layout, so F::Of<'a> and F::Of<'static> have identical
+ // representation; casting the slot pointer is sound.
+ let data = unsafe {
+ pin_init::pin_init_from_closure::<F::Of<'static>, E>(move |slot| {
+ data.__pinned_init(slot.cast())
+ })
+ };
+
+ Ok(Self(Devres::new(dev, data)?))
+ }
+
+ /// Return a reference of the [`Device`] this [`DevresLt`] instance has been created with.
+ #[inline]
+ pub fn device(&self) -> &Device {
+ self.0.device()
+ }
+
+ /// Obtain `&F::Of<'_>`, bypassing the [`Revocable`], through a closure.
+ ///
+ /// This method works like [`DevresLt::access`](DevresLt::access) but accepts any
+ /// [`trait@ForLt`] type, not just [`trait@CovariantForLt`].
+ #[inline]
+ pub fn access_with<R, G>(&self, dev: &Device<Bound>, f: G) -> Result<R>
+ where
+ G: for<'a> FnOnce(&F::Of<'a>) -> R,
+ {
+ self.0.access(dev).map(f)
+ }
+
+ /// [`DevresLt`] accessor for [`Revocable::try_access_with`].
+ #[inline]
+ pub fn try_access_with<R, G>(&self, f: G) -> Option<R>
+ where
+ G: for<'a> FnOnce(&F::Of<'a>) -> R,
+ {
+ self.0.data().try_access_with(f)
+ }
+}
+
+impl<F: CovariantForLt> DevresLt<F>
+where
+ for<'a> F::Of<'a>: Send,
+{
+ /// Obtain `&'a F::Of<'a>`, bypassing the [`Revocable`].
+ ///
+ /// This method works like [`Devres::access`], but shortens the returned reference's lifetime
+ /// from `'static` to `'a` via [`CovariantForLt::cast_ref`].
+ #[inline]
+ pub fn access<'a>(&'a self, dev: &'a Device<Bound>) -> Result<&'a F::Of<'a>> {
+ self.0.access(dev).map(F::cast_ref)
+ }
+
+ /// [`DevresLt`] accessor for [`Revocable::try_access`].
+ #[inline]
+ pub fn try_access(&self) -> Option<DevresGuard<'_, F>> {
+ self.0.data().try_access().map(DevresGuard)
+ }
+}
+
/// Consume `data` and [`Drop::drop`] `data` once `dev` is unbound.
fn register_foreign<P>(dev: &Device<Bound>, data: P) -> Result
where
diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs
index fc2a3e24f8d5..931f2fa3bb10 100644
--- a/rust/kernel/io/mem.rs
+++ b/rust/kernel/io/mem.rs
@@ -9,7 +9,7 @@ use crate::{
Bound,
Device, //
},
- devres::Devres,
+ devres::DevresLt,
io::{
self,
resource::{
@@ -20,6 +20,10 @@ use crate::{
MmioRaw, //
},
prelude::*,
+ types::{
+ CovariantForLt,
+ ForLt, //
+ },
};
/// An IO request for a specific device and resource.
@@ -172,6 +176,19 @@ pub struct ExclusiveIoMem<'a, const SIZE: usize> {
_region: Region,
}
+impl<const SIZE: usize> ForLt for ExclusiveIoMem<'static, SIZE> {
+ type Of<'a> = ExclusiveIoMem<'a, SIZE>;
+}
+
+// SAFETY: `ExclusiveIoMem<'a, SIZE>` is covariant over `'a`; it holds an `IoMem<'a, SIZE>`,
+// which holds `&'a Device<Bound>`, which is covariant.
+unsafe impl<const SIZE: usize> CovariantForLt for ExclusiveIoMem<'static, SIZE> {}
+
+/// A device-managed exclusive I/O memory region.
+///
+/// See [`ExclusiveIoMem::into_devres`].
+pub type DevresExclusiveIoMem<const SIZE: usize> = DevresLt<ExclusiveIoMem<'static, SIZE>>;
+
impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> {
/// Creates a new `ExclusiveIoMem` instance.
fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
@@ -198,15 +215,13 @@ impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> {
/// Consume the `ExclusiveIoMem` and register it as a device-managed resource.
///
- /// The returned `Devres<ExclusiveIoMem<'static, SIZE>>` can outlive the original lifetime
- /// `'a`. Access to the I/O memory is revoked when the device is unbound.
- pub fn into_devres(self) -> Result<Devres<ExclusiveIoMem<'static, SIZE>>> {
- // SAFETY: Casting to `'static` is sound because `Devres` guarantees the
- // `ExclusiveIoMem` does not actually outlive the device -- access is revoked and the
- // resource is released when the device is unbound.
- let iomem: ExclusiveIoMem<'static, SIZE> = unsafe { core::mem::transmute(self) };
- let dev = iomem.iomem.dev;
- Devres::new(dev, iomem)
+ /// The returned [`DevresExclusiveIoMem`] can outlive the original borrow and be stored in
+ /// driver data. Access to the I/O memory is revoked automatically when the device is unbound.
+ pub fn into_devres(self) -> Result<DevresExclusiveIoMem<SIZE>> {
+ let dev = self.iomem.dev;
+ // SAFETY: `ExclusiveIoMem` only holds a device reference and an I/O mapping, both of
+ // which remain valid for the device's full bound scope, not just for `'a`.
+ unsafe { DevresLt::new(dev, self) }
}
}
@@ -232,6 +247,19 @@ pub struct IoMem<'a, const SIZE: usize = 0> {
io: MmioRaw<SIZE>,
}
+impl<const SIZE: usize> ForLt for IoMem<'static, SIZE> {
+ type Of<'a> = IoMem<'a, SIZE>;
+}
+
+// SAFETY: `IoMem<'a, SIZE>` is covariant over `'a`; it holds `&'a Device<Bound>`,
+// which is covariant.
+unsafe impl<const SIZE: usize> CovariantForLt for IoMem<'static, SIZE> {}
+
+/// A device-managed I/O memory region.
+///
+/// See [`IoMem::into_devres`].
+pub type DevresIoMem<const SIZE: usize = 0> = DevresLt<IoMem<'static, SIZE>>;
+
impl<'a, const SIZE: usize> IoMem<'a, SIZE> {
fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
// Note: Some ioremap() implementations use types that depend on the CPU
@@ -271,16 +299,13 @@ impl<'a, const SIZE: usize> IoMem<'a, SIZE> {
/// Consume the `IoMem` and register it as a device-managed resource.
///
- /// The returned `Devres<IoMem<'static, SIZE>>` can outlive the original
- /// lifetime `'a`. Access to the I/O memory is revoked when the device
- /// is unbound.
- pub fn into_devres(self) -> Result<Devres<IoMem<'static, SIZE>>> {
- // SAFETY: Casting to `'static` is sound because `Devres` guarantees the `IoMem` does not
- // actually outlive the device -- access is revoked and the resource is released when the
- // device is unbound.
- let iomem: IoMem<'static, SIZE> = unsafe { core::mem::transmute(self) };
- let dev = iomem.dev;
- Devres::new(dev, iomem)
+ /// The returned [`DevresIoMem`] can outlive the original borrow and be stored in driver data.
+ /// Access to the I/O memory is revoked automatically when the device is unbound.
+ pub fn into_devres(self) -> Result<DevresIoMem<SIZE>> {
+ let dev = self.dev;
+ // SAFETY: `IoMem` only holds a device reference and an I/O mapping, both of which
+ // remain valid for the device's full bound scope, not just for `'a`.
+ unsafe { DevresLt::new(dev, self) }
}
}
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 5071cae6543f..f783b9d9fa26 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -45,6 +45,7 @@ pub use self::io::{
ConfigSpace,
ConfigSpaceKind,
ConfigSpaceSize,
+ DevresBar,
Extended,
Normal, //
};
diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs
index 0461e01aaa20..6ebfbf368cd3 100644
--- a/rust/kernel/pci/io.rs
+++ b/rust/kernel/pci/io.rs
@@ -6,7 +6,7 @@ use super::Device;
use crate::{
bindings,
device,
- devres::Devres,
+ devres::DevresLt,
io::{
Io,
IoCapable,
@@ -14,7 +14,11 @@ use crate::{
Mmio,
MmioRaw, //
},
- prelude::*, //
+ prelude::*,
+ types::{
+ CovariantForLt,
+ ForLt, //
+ }, //
};
use core::{
marker::PhantomData,
@@ -151,6 +155,19 @@ pub struct Bar<'a, const SIZE: usize = 0> {
num: i32,
}
+impl<const SIZE: usize> ForLt for Bar<'static, SIZE> {
+ type Of<'a> = Bar<'a, SIZE>;
+}
+
+// SAFETY: `Bar<'a, SIZE>` is covariant over `'a`; it holds `&'a Device<Bound>`,
+// which is covariant.
+unsafe impl<const SIZE: usize> CovariantForLt for Bar<'static, SIZE> {}
+
+/// A device-managed PCI BAR mapping.
+///
+/// See [`Bar::into_devres`].
+pub type DevresBar<const SIZE: usize = 0> = DevresLt<Bar<'static, SIZE>>;
+
impl<'a, const SIZE: usize> Bar<'a, SIZE> {
pub(super) fn new(
pdev: &'a Device<device::Bound>,
@@ -223,15 +240,13 @@ impl<'a, const SIZE: usize> Bar<'a, SIZE> {
/// Consume the `Bar` and register it as a device-managed resource.
///
- /// The returned `Devres<Bar<'static, SIZE>>` can outlive the original lifetime `'a`. Access
- /// to the BAR is revoked when the device is unbound.
- pub fn into_devres(self) -> Result<Devres<Bar<'static, SIZE>>> {
- // SAFETY: Casting to `'static` is sound because `Devres` guarantees the `Bar` does not
- // actually outlive the device -- access is revoked and the resource is released when the
- // device is unbound.
- let bar: Bar<'static, SIZE> = unsafe { core::mem::transmute(self) };
- let pdev = bar.pdev;
- Devres::new(pdev.as_ref(), bar)
+ /// The returned [`DevresBar`] can outlive the original borrow and be stored in driver data.
+ /// Access to the BAR is revoked automatically when the device is unbound.
+ pub fn into_devres(self) -> Result<DevresBar<SIZE>> {
+ let pdev = self.pdev;
+ // SAFETY: `Bar` only holds a reference to the device and an I/O mapping, both of which
+ // remain valid for the device's full bound scope, not just for `'a`.
+ unsafe { DevresLt::new(pdev.as_ref(), self) }
}
}
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index ac316fd7b538..699aabe01ee5 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -13,7 +13,10 @@ use pin_init::{PinInit, Wrapper, Zeroable};
#[doc(hidden)]
pub mod for_lt;
-pub use for_lt::ForLt;
+pub use for_lt::{
+ CovariantForLt,
+ ForLt, //
+};
/// Used to transfer ownership to and from foreign (non-Rust) languages.
///
diff --git a/rust/kernel/types/for_lt.rs b/rust/kernel/types/for_lt.rs
index d44323c28e8d..b8f422c802dc 100644
--- a/rust/kernel/types/for_lt.rs
+++ b/rust/kernel/types/for_lt.rs
@@ -1,22 +1,19 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
-//! Provide implementation and test of the `ForLt` trait and macro.
+//! Provide implementation and test of the [`trait@ForLt`] and [`trait@CovariantForLt`] traits and
+//! macros.
//!
-//! This module is hidden and user should just use `ForLt!` directly.
+//! This module is hidden and users should just use [`ForLt!`](macro@ForLt) /
+//! [`CovariantForLt!`](macro@CovariantForLt) 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.
+/// It is not recommended to implement this trait directly. [`ForLt!`](macro@ForLt) macro is
+/// provided to obtain a type that implements this trait.
///
/// The full syntax is
///
@@ -49,16 +46,65 @@ use core::marker::PhantomData;
/// ForLt!(u32) // Equivalent to `ForLt!(for<'a> u32)`.
/// # >();
/// ```
+pub trait ForLt {
+ /// The type parameterized by the lifetime.
+ type Of<'a>: 'a;
+}
+pub use macros::ForLt;
+
+/// [`trait@ForLt`] subtrait for types that are covariant over their lifetime parameter.
+///
+/// Provides a safe [`cast_ref`](CovariantForLt::cast_ref) method for types that are proven to be
+/// covariant. The `CovariantForLt!` macro syntax is the same as `ForLt!`.
+///
+/// # Macro
+///
+/// It is not recommended to implement this trait directly.
+/// [`CovariantForLt!`](macro@CovariantForLt) macro is provided to obtain a type that implements
+/// this trait.
+///
+/// The full syntax is
+///
+/// ```
+/// # use kernel::types::CovariantForLt;
+/// # fn expect_lt<F: CovariantForLt>() {}
+/// # struct TypeThatUse<'a>(&'a ());
+/// # expect_lt::<
+/// CovariantForLt!(for<'a> TypeThatUse<'a>)
+/// # >();
+/// ```
+///
+/// which gives a type so that
+/// `<CovariantForLt!(for<'a> TypeThatUse<'a>) as CovariantForLt>::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::CovariantForLt;
+/// # fn expect_lt<F: CovariantForLt>() {}
+/// # struct TypeThatUse<'a>(&'a ());
+/// # expect_lt::<
+/// CovariantForLt!(TypeThatUse<'_>) // Equivalent to `CovariantForLt!(for<'a> TypeThatUse<'a>)`.
+/// # >();
+/// # expect_lt::<
+/// CovariantForLt!(&u32) // Equivalent to `CovariantForLt!(for<'a> &'a u32)`.
+/// # >();
+/// # expect_lt::<
+/// CovariantForLt!(u32) // Equivalent to `CovariantForLt!(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>() {}
+/// # use kernel::types::CovariantForLt;
+/// # fn expect_lt<F: CovariantForLt>() {}
/// # expect_lt::<
-/// ForLt!(fn(&u32)) // Contravariant, will fail compilation.
+/// CovariantForLt!(fn(&u32)) // Contravariant, will fail compilation.
/// # >();
/// ```
///
@@ -67,26 +113,23 @@ use core::marker::PhantomData;
/// the generic parameter but is in a separate item.
///
/// ```
-/// # use kernel::types::ForLt;
-/// fn expect_lt<F: ForLt>() {}
+/// # use kernel::types::CovariantForLt;
+/// fn expect_lt<F: CovariantForLt>() {}
/// # #[allow(clippy::unnecessary_safety_comment, reason = "false positive")]
/// fn generic_fn<T: 'static>() {
/// // Syntactically proven by the macro
-/// expect_lt::<ForLt!(&T)>();
+/// expect_lt::<CovariantForLt!(&T)>();
/// // Syntactically proven by the macro
-/// expect_lt::<ForLt!(&KBox<T>)>();
+/// expect_lt::<CovariantForLt!(&KBox<T>)>();
/// // Cannot be syntactically proven, need to check covariance of `KBox`
-/// // expect_lt::<ForLt!(&KBox<&T>)>();
+/// // expect_lt::<CovariantForLt!(&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;
-
+pub unsafe trait CovariantForLt: ForLt {
/// 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> {
@@ -94,29 +137,33 @@ pub unsafe trait ForLt {
unsafe { core::mem::transmute(long) }
}
}
-pub use macros::ForLt;
+pub use macros::CovariantForLt;
/// This is intended to be an "unsafe-to-refer-to" type.
///
-/// Must only be used by the `ForLt!` macro.
+/// Must only be used by the [`ForLt!`](macro@ForLt) / [`CovariantForLt!`](macro@CovariantForLt)
+/// macros.
///
/// `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.
+/// additional properties. [`ForLt!`](macro@ForLt) emits `N = 0`;
+/// [`CovariantForLt!`](macro@CovariantForLt) emits `N = 1` after a covariance proof.
#[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.
+// This is a helper trait for implementation of `ForLt` / `CovariantForLt` 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> {
+impl<T: ?Sized + for<'a> WithLt<'a>, WF, const N: usize> ForLt for UnsafeForLtImpl<T, WF, N> {
type Of<'a> = <T as WithLt<'a>>::Of;
}
+
+// SAFETY: In `CovariantForLt!` macro, a covariance proof is generated in the `N` const generic
+// and it will fail to evaluate if the type is not covariant. Only `N = 1` gets this impl.
+unsafe impl<T: ?Sized + for<'a> WithLt<'a>, WF> CovariantForLt for UnsafeForLtImpl<T, WF, 1> {}