mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
rust: init: add assert_pinned
macro
Add a macro to statically check if a field of a struct is marked with `#[pin]` ie that it is structurally pinned. This can be used when `unsafe` code needs to rely on fields being structurally pinned. The macro has a special "inline" mode for the case where the type depends on generic parameters from the surrounding scope. Signed-off-by: Benno Lossin <benno.lossin@proton.me> Co-developed-by: Alice Ryhl <aliceryhl@google.com> Signed-off-by: Alice Ryhl <aliceryhl@google.com> Link: https://lore.kernel.org/r/20240814-linked-list-v5-1-f5f5e8075da0@google.com [ Replaced `compile_fail` with `ignore` and a TODO note. Removed `pub` from example to clean `unreachable_pub` lint. - Miguel ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
parent
c6945acad7
commit
0528ca0a4f
@ -743,6 +743,74 @@ macro_rules! try_init {
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is
|
||||
/// structurally pinned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// This will succeed:
|
||||
/// ```
|
||||
/// use kernel::assert_pinned;
|
||||
/// #[pin_data]
|
||||
/// struct MyStruct {
|
||||
/// #[pin]
|
||||
/// some_field: u64,
|
||||
/// }
|
||||
///
|
||||
/// assert_pinned!(MyStruct, some_field, u64);
|
||||
/// ```
|
||||
///
|
||||
/// This will fail:
|
||||
// TODO: replace with `compile_fail` when supported.
|
||||
/// ```ignore
|
||||
/// use kernel::assert_pinned;
|
||||
/// #[pin_data]
|
||||
/// struct MyStruct {
|
||||
/// some_field: u64,
|
||||
/// }
|
||||
///
|
||||
/// assert_pinned!(MyStruct, some_field, u64);
|
||||
/// ```
|
||||
///
|
||||
/// Some uses of the macro may trigger the `can't use generic parameters from outer item` error. To
|
||||
/// work around this, you may pass the `inline` parameter to the macro. The `inline` parameter can
|
||||
/// only be used when the macro is invoked from a function body.
|
||||
/// ```
|
||||
/// use kernel::assert_pinned;
|
||||
/// #[pin_data]
|
||||
/// struct Foo<T> {
|
||||
/// #[pin]
|
||||
/// elem: T,
|
||||
/// }
|
||||
///
|
||||
/// impl<T> Foo<T> {
|
||||
/// fn project(self: Pin<&mut Self>) -> Pin<&mut T> {
|
||||
/// assert_pinned!(Foo<T>, elem, T, inline);
|
||||
///
|
||||
/// // SAFETY: The field is structurally pinned.
|
||||
/// unsafe { self.map_unchecked_mut(|me| &mut me.elem) }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! assert_pinned {
|
||||
($ty:ty, $field:ident, $field_ty:ty, inline) => {
|
||||
let _ = move |ptr: *mut $field_ty| {
|
||||
// SAFETY: This code is unreachable.
|
||||
let data = unsafe { <$ty as $crate::init::__internal::HasPinData>::__pin_data() };
|
||||
let init = $crate::init::__internal::AlwaysFail::<$field_ty>::new();
|
||||
// SAFETY: This code is unreachable.
|
||||
unsafe { data.$field(ptr, init) }.ok();
|
||||
};
|
||||
};
|
||||
|
||||
($ty:ty, $field:ident, $field_ty:ty) => {
|
||||
const _: () = {
|
||||
$crate::assert_pinned!($ty, $field, $field_ty, inline);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// A pin-initializer for the type `T`.
|
||||
///
|
||||
/// To use this initializer, you will need a suitable memory location that can hold a `T`. This can
|
||||
|
@ -228,3 +228,32 @@ impl OnlyCallFromDrop {
|
||||
Self(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializer that always fails.
|
||||
///
|
||||
/// Used by [`assert_pinned!`].
|
||||
///
|
||||
/// [`assert_pinned!`]: crate::assert_pinned
|
||||
pub struct AlwaysFail<T: ?Sized> {
|
||||
_t: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> AlwaysFail<T> {
|
||||
/// Creates a new initializer that always fails.
|
||||
pub fn new() -> Self {
|
||||
Self { _t: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Default for AlwaysFail<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `__pinned_init` always fails, which is always okay.
|
||||
unsafe impl<T: ?Sized> PinInit<T, ()> for AlwaysFail<T> {
|
||||
unsafe fn __pinned_init(self, _slot: *mut T) -> Result<(), ()> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user