linux/rust/macros/pinned_drop.rs
Benno Lossin d0fdc39612 rust: init: add PinnedDrop trait and macros
The `PinnedDrop` trait that facilitates destruction of pinned types.
It has to be implemented via the `#[pinned_drop]` macro, since the
`drop` function should not be called by normal code, only by other
destructors. It also only works on structs that are annotated with
`#[pin_data(PinnedDrop)]`.

Co-developed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Andreas Hindborg <a.hindborg@samsung.com>
Link: https://lore.kernel.org/r/20230408122429.1103522-10-y86-dev@protonmail.com
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
2023-04-12 18:41:05 +02:00

50 lines
1.8 KiB
Rust

// SPDX-License-Identifier: Apache-2.0 OR MIT
use proc_macro::{TokenStream, TokenTree};
pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream {
let mut toks = input.into_iter().collect::<Vec<_>>();
assert!(!toks.is_empty());
// Ensure that we have an `impl` item.
assert!(matches!(&toks[0], TokenTree::Ident(i) if i.to_string() == "impl"));
// Ensure that we are implementing `PinnedDrop`.
let mut nesting: usize = 0;
let mut pinned_drop_idx = None;
for (i, tt) in toks.iter().enumerate() {
match tt {
TokenTree::Punct(p) if p.as_char() == '<' => {
nesting += 1;
}
TokenTree::Punct(p) if p.as_char() == '>' => {
nesting = nesting.checked_sub(1).unwrap();
continue;
}
_ => {}
}
if i >= 1 && nesting == 0 {
// Found the end of the generics, this should be `PinnedDrop`.
assert!(
matches!(tt, TokenTree::Ident(i) if i.to_string() == "PinnedDrop"),
"expected 'PinnedDrop', found: '{:?}'",
tt
);
pinned_drop_idx = Some(i);
break;
}
}
let idx = pinned_drop_idx
.unwrap_or_else(|| panic!("Expected an `impl` block implementing `PinnedDrop`."));
// Fully qualify the `PinnedDrop`, as to avoid any tampering.
toks.splice(idx..idx, quote!(::kernel::init::));
// Take the `{}` body and call the declarative macro.
if let Some(TokenTree::Group(last)) = toks.pop() {
let last = last.stream();
quote!(::kernel::__pinned_drop! {
@impl_sig(#(#toks)*),
@impl_body(#last),
})
} else {
TokenStream::from_iter(toks)
}
}