Skip to content

Commit 0528ca0

Browse files
BennoLossinojeda
authored andcommitted
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 <[email protected]> Co-developed-by: Alice Ryhl <[email protected]> Signed-off-by: Alice Ryhl <[email protected]> Link: https://lore.kernel.org/r/[email protected] [ Replaced `compile_fail` with `ignore` and a TODO note. Removed `pub` from example to clean `unreachable_pub` lint. - Miguel ] Signed-off-by: Miguel Ojeda <[email protected]>
1 parent c6945ac commit 0528ca0

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

rust/kernel/init.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,74 @@ macro_rules! try_init {
743743
};
744744
}
745745

746+
/// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is
747+
/// structurally pinned.
748+
///
749+
/// # Example
750+
///
751+
/// This will succeed:
752+
/// ```
753+
/// use kernel::assert_pinned;
754+
/// #[pin_data]
755+
/// struct MyStruct {
756+
/// #[pin]
757+
/// some_field: u64,
758+
/// }
759+
///
760+
/// assert_pinned!(MyStruct, some_field, u64);
761+
/// ```
762+
///
763+
/// This will fail:
764+
// TODO: replace with `compile_fail` when supported.
765+
/// ```ignore
766+
/// use kernel::assert_pinned;
767+
/// #[pin_data]
768+
/// struct MyStruct {
769+
/// some_field: u64,
770+
/// }
771+
///
772+
/// assert_pinned!(MyStruct, some_field, u64);
773+
/// ```
774+
///
775+
/// Some uses of the macro may trigger the `can't use generic parameters from outer item` error. To
776+
/// work around this, you may pass the `inline` parameter to the macro. The `inline` parameter can
777+
/// only be used when the macro is invoked from a function body.
778+
/// ```
779+
/// use kernel::assert_pinned;
780+
/// #[pin_data]
781+
/// struct Foo<T> {
782+
/// #[pin]
783+
/// elem: T,
784+
/// }
785+
///
786+
/// impl<T> Foo<T> {
787+
/// fn project(self: Pin<&mut Self>) -> Pin<&mut T> {
788+
/// assert_pinned!(Foo<T>, elem, T, inline);
789+
///
790+
/// // SAFETY: The field is structurally pinned.
791+
/// unsafe { self.map_unchecked_mut(|me| &mut me.elem) }
792+
/// }
793+
/// }
794+
/// ```
795+
#[macro_export]
796+
macro_rules! assert_pinned {
797+
($ty:ty, $field:ident, $field_ty:ty, inline) => {
798+
let _ = move |ptr: *mut $field_ty| {
799+
// SAFETY: This code is unreachable.
800+
let data = unsafe { <$ty as $crate::init::__internal::HasPinData>::__pin_data() };
801+
let init = $crate::init::__internal::AlwaysFail::<$field_ty>::new();
802+
// SAFETY: This code is unreachable.
803+
unsafe { data.$field(ptr, init) }.ok();
804+
};
805+
};
806+
807+
($ty:ty, $field:ident, $field_ty:ty) => {
808+
const _: () = {
809+
$crate::assert_pinned!($ty, $field, $field_ty, inline);
810+
};
811+
};
812+
}
813+
746814
/// A pin-initializer for the type `T`.
747815
///
748816
/// To use this initializer, you will need a suitable memory location that can hold a `T`. This can

rust/kernel/init/__internal.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,32 @@ impl OnlyCallFromDrop {
228228
Self(())
229229
}
230230
}
231+
232+
/// Initializer that always fails.
233+
///
234+
/// Used by [`assert_pinned!`].
235+
///
236+
/// [`assert_pinned!`]: crate::assert_pinned
237+
pub struct AlwaysFail<T: ?Sized> {
238+
_t: PhantomData<T>,
239+
}
240+
241+
impl<T: ?Sized> AlwaysFail<T> {
242+
/// Creates a new initializer that always fails.
243+
pub fn new() -> Self {
244+
Self { _t: PhantomData }
245+
}
246+
}
247+
248+
impl<T: ?Sized> Default for AlwaysFail<T> {
249+
fn default() -> Self {
250+
Self::new()
251+
}
252+
}
253+
254+
// SAFETY: `__pinned_init` always fails, which is always okay.
255+
unsafe impl<T: ?Sized> PinInit<T, ()> for AlwaysFail<T> {
256+
unsafe fn __pinned_init(self, _slot: *mut T) -> Result<(), ()> {
257+
Err(())
258+
}
259+
}

0 commit comments

Comments
 (0)