mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
rust: init: add initialization macros
Add the following initializer macros: - `#[pin_data]` to annotate structurally pinned fields of structs, needed for `pin_init!` and `try_pin_init!` to select the correct initializer of fields. - `pin_init!` create a pin-initializer for a struct with the `Infallible` error type. - `try_pin_init!` create a pin-initializer for a struct with a custom error type (`kernel::error::Error` is the default). - `init!` create an in-place-initializer for a struct with the `Infallible` error type. - `try_init!` create an in-place-initializer for a struct with a custom error type (`kernel::error::Error` is the default). Also add their needed internal helper traits and structs. 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-8-y86-dev@protonmail.com [ Fixed three typos. ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
committed by
Miguel Ojeda
parent
90e53c5e70
commit
fc6c6baa1f
File diff suppressed because it is too large
Load Diff
@@ -31,3 +31,133 @@ where
|
||||
(self.0)(slot)
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait is only implemented via the `#[pin_data]` proc-macro. It is used to facilitate
|
||||
/// the pin projections within the initializers.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only the `init` module is allowed to use this trait.
|
||||
pub unsafe trait HasPinData {
|
||||
type PinData: PinData;
|
||||
|
||||
unsafe fn __pin_data() -> Self::PinData;
|
||||
}
|
||||
|
||||
/// Marker trait for pinning data of structs.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only the `init` module is allowed to use this trait.
|
||||
pub unsafe trait PinData: Copy {
|
||||
type Datee: ?Sized + HasPinData;
|
||||
|
||||
/// Type inference helper function.
|
||||
fn make_closure<F, O, E>(self, f: F) -> F
|
||||
where
|
||||
F: FnOnce(*mut Self::Datee) -> Result<O, E>,
|
||||
{
|
||||
f
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait is automatically implemented for every type. It aims to provide the same type
|
||||
/// inference help as `HasPinData`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only the `init` module is allowed to use this trait.
|
||||
pub unsafe trait HasInitData {
|
||||
type InitData: InitData;
|
||||
|
||||
unsafe fn __init_data() -> Self::InitData;
|
||||
}
|
||||
|
||||
/// Same function as `PinData`, but for arbitrary data.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only the `init` module is allowed to use this trait.
|
||||
pub unsafe trait InitData: Copy {
|
||||
type Datee: ?Sized + HasInitData;
|
||||
|
||||
/// Type inference helper function.
|
||||
fn make_closure<F, O, E>(self, f: F) -> F
|
||||
where
|
||||
F: FnOnce(*mut Self::Datee) -> Result<O, E>,
|
||||
{
|
||||
f
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AllData<T: ?Sized>(PhantomData<fn(Box<T>) -> Box<T>>);
|
||||
|
||||
impl<T: ?Sized> Clone for AllData<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Copy for AllData<T> {}
|
||||
|
||||
unsafe impl<T: ?Sized> InitData for AllData<T> {
|
||||
type Datee = T;
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized> HasInitData for T {
|
||||
type InitData = AllData<T>;
|
||||
|
||||
unsafe fn __init_data() -> Self::InitData {
|
||||
AllData(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
/// When a value of this type is dropped, it drops a `T`.
|
||||
///
|
||||
/// Can be forgotten to prevent the drop.
|
||||
pub struct DropGuard<T: ?Sized> {
|
||||
ptr: *mut T,
|
||||
do_drop: Cell<bool>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DropGuard<T> {
|
||||
/// Creates a new [`DropGuard<T>`]. It will [`ptr::drop_in_place`] `ptr` when it gets dropped.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `ptr` must be a valid pointer.
|
||||
///
|
||||
/// It is the callers responsibility that `self` will only get dropped if the pointee of `ptr`:
|
||||
/// - has not been dropped,
|
||||
/// - is not accessible by any other means,
|
||||
/// - will not be dropped by any other means.
|
||||
#[inline]
|
||||
pub unsafe fn new(ptr: *mut T) -> Self {
|
||||
Self {
|
||||
ptr,
|
||||
do_drop: Cell::new(true),
|
||||
}
|
||||
}
|
||||
|
||||
/// Prevents this guard from dropping the supplied pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe in order to prevent safe code from forgetting this guard. It should
|
||||
/// only be called by the macros in this module.
|
||||
#[inline]
|
||||
pub unsafe fn forget(&self) {
|
||||
self.do_drop.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for DropGuard<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if self.do_drop.get() {
|
||||
// SAFETY: A `DropGuard` can only be constructed using the unsafe `new` function
|
||||
// ensuring that this operation is safe.
|
||||
unsafe { ptr::drop_in_place(self.ptr) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
707
rust/kernel/init/macros.rs
Normal file
707
rust/kernel/init/macros.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ mod quote;
|
||||
mod concat_idents;
|
||||
mod helpers;
|
||||
mod module;
|
||||
mod pin_data;
|
||||
mod vtable;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
@@ -168,3 +169,31 @@ pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream {
|
||||
pub fn concat_idents(ts: TokenStream) -> TokenStream {
|
||||
concat_idents::concat_idents(ts)
|
||||
}
|
||||
|
||||
/// Used to specify the pinning information of the fields of a struct.
|
||||
///
|
||||
/// This is somewhat similar in purpose as
|
||||
/// [pin-project-lite](https://crates.io/crates/pin-project-lite).
|
||||
/// Place this macro on a struct definition and then `#[pin]` in front of the attributes of each
|
||||
/// field you want to structurally pin.
|
||||
///
|
||||
/// This macro enables the use of the [`pin_init!`] macro. When pin-initializing a `struct`,
|
||||
/// then `#[pin]` directs the type of initializer that is required.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[pin_data]
|
||||
/// struct DriverData {
|
||||
/// #[pin]
|
||||
/// queue: Mutex<Vec<Command>>,
|
||||
/// buf: Box<[u8; 1024 * 1024]>,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`pin_init!`]: ../kernel/macro.pin_init.html
|
||||
// ^ cannot use direct link, since `kernel` is not a dependency of `macros`.
|
||||
#[proc_macro_attribute]
|
||||
pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {
|
||||
pin_data::pin_data(inner, item)
|
||||
}
|
||||
|
||||
79
rust/macros/pin_data.rs
Normal file
79
rust/macros/pin_data.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
use proc_macro::{Punct, Spacing, TokenStream, TokenTree};
|
||||
|
||||
pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
// This proc-macro only does some pre-parsing and then delegates the actual parsing to
|
||||
// `kernel::__pin_data!`.
|
||||
//
|
||||
// In here we only collect the generics, since parsing them in declarative macros is very
|
||||
// elaborate. We also do not need to analyse their structure, we only need to collect them.
|
||||
|
||||
// `impl_generics`, the declared generics with their bounds.
|
||||
let mut impl_generics = vec![];
|
||||
// Only the names of the generics, without any bounds.
|
||||
let mut ty_generics = vec![];
|
||||
// Tokens not related to the generics e.g. the `impl` token.
|
||||
let mut rest = vec![];
|
||||
// The current level of `<`.
|
||||
let mut nesting = 0;
|
||||
let mut toks = input.into_iter();
|
||||
// If we are at the beginning of a generic parameter.
|
||||
let mut at_start = true;
|
||||
for tt in &mut toks {
|
||||
match tt.clone() {
|
||||
TokenTree::Punct(p) if p.as_char() == '<' => {
|
||||
if nesting >= 1 {
|
||||
impl_generics.push(tt);
|
||||
}
|
||||
nesting += 1;
|
||||
}
|
||||
TokenTree::Punct(p) if p.as_char() == '>' => {
|
||||
if nesting == 0 {
|
||||
break;
|
||||
} else {
|
||||
nesting -= 1;
|
||||
if nesting >= 1 {
|
||||
impl_generics.push(tt);
|
||||
}
|
||||
if nesting == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
tt => {
|
||||
if nesting == 1 {
|
||||
match &tt {
|
||||
TokenTree::Ident(i) if i.to_string() == "const" => {}
|
||||
TokenTree::Ident(_) if at_start => {
|
||||
ty_generics.push(tt.clone());
|
||||
ty_generics.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
|
||||
at_start = false;
|
||||
}
|
||||
TokenTree::Punct(p) if p.as_char() == ',' => at_start = true,
|
||||
TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {
|
||||
ty_generics.push(tt.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if nesting >= 1 {
|
||||
impl_generics.push(tt);
|
||||
} else if nesting == 0 {
|
||||
rest.push(tt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rest.extend(toks);
|
||||
// This should be the body of the struct `{...}`.
|
||||
let last = rest.pop();
|
||||
quote!(::kernel::__pin_data! {
|
||||
parse_input:
|
||||
@args(#args),
|
||||
@sig(#(#rest)*),
|
||||
@impl_generics(#(#impl_generics)*),
|
||||
@ty_generics(#(#ty_generics)*),
|
||||
@body(#last),
|
||||
})
|
||||
}
|
||||
@@ -38,7 +38,6 @@ impl ToTokens for TokenStream {
|
||||
/// This is a similar to the
|
||||
/// [`quote_spanned!`](https://docs.rs/quote/latest/quote/macro.quote_spanned.html) macro from the
|
||||
/// `quote` crate but provides only just enough functionality needed by the current `macros` crate.
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! quote_spanned {
|
||||
($span:expr => $($tt:tt)*) => {
|
||||
#[allow(clippy::vec_init_then_push)]
|
||||
@@ -137,7 +136,6 @@ macro_rules! quote_spanned {
|
||||
/// `macros` crate.
|
||||
///
|
||||
/// [`Span::mixed_site()`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.mixed_site
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! quote {
|
||||
($($tt:tt)*) => {
|
||||
quote_spanned!(::proc_macro::Span::mixed_site() => $($tt)*)
|
||||
|
||||
Reference in New Issue
Block a user