From f26f2f22c6d16d6019fbd1248681e57a1a533bd3 Mon Sep 17 00:00:00 2001 From: Luiz Georg Date: Fri, 10 Jul 2026 17:20:31 +0100 Subject: rust: pin-init: internal: error on duplicate `#[pin]` attribute Duplicated `#[pin]` has no effect, thus error if misused. Reported-by: Mohamad Alsadhan Closes: https://github.com/Rust-for-Linux/pin-init/issues/119 Signed-off-by: Luiz Georg Link: https://patch.msgid.link/20260710-pin-init-sync-v1-1-8fa16cde87ae@garyguo.net [ Reworded commit message, and change the logic so code generation still continue after reporting error - Gary ] Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 9fbbd25bcaac..263f67300727 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -85,7 +85,10 @@ pub(crate) fn pin_data( .map(|field| { let len = field.attrs.len(); field.attrs.retain(|a| !a.path().is_ident("pin")); - let pinned = len != field.attrs.len(); + let pinned_count = len - field.attrs.len(); + if pinned_count > 1 { + dcx.error(&field, "#[pin] attribute specified more than once"); + } let cfg_attrs = field .attrs @@ -95,7 +98,7 @@ pub(crate) fn pin_data( FieldInfo { field: &*field, - pinned, + pinned: pinned_count != 0, cfg_attrs, } }) -- cgit v1.2.3 From 554d1afffc391f938ca58ab74b82238ac2d29c37 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 10 Jul 2026 17:20:32 +0100 Subject: rust: pin-init: examples: fix incorrect drop Remove the drop and associated clippy allow. The warning reported by Clippy here is genuine; the binding created is `Pin<&mut T>` so dropping it does nothing. `stack_pin_init` created bindings are only dropped at the end of scope. Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260710-pin-init-sync-v1-2-8fa16cde87ae@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/examples/mutex.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/examples/mutex.rs b/rust/pin-init/examples/mutex.rs index 35ecb5f68dc3..882f3e23f5dd 100644 --- a/rust/pin-init/examples/mutex.rs +++ b/rust/pin-init/examples/mutex.rs @@ -91,7 +91,7 @@ impl CMutex { pub fn lock(&self) -> Pin> { let mut sguard = self.spin_lock.acquire(); if self.locked.get() { - stack_pin_init!(let wait_entry = WaitEntry::insert_new(&self.wait_list)); + stack_pin_init!(let _wait_entry = WaitEntry::insert_new(&self.wait_list)); // println!("wait list length: {}", self.wait_list.size()); while self.locked.get() { drop(sguard); @@ -99,9 +99,6 @@ impl CMutex { thread::park(); sguard = self.spin_lock.acquire(); } - // This does have an effect, as the ListHead inside wait_entry implements Drop! - #[expect(clippy::drop_non_drop)] - drop(wait_entry); } self.locked.set(true); unsafe { -- cgit v1.2.3 From 690d82bf1da92f0a073df3728b76d4dbc99b5172 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 10 Jul 2026 17:20:33 +0100 Subject: rust: pin-init: remove redundant clippy expects in doc tests These lints are automatically suppressed inside doc tests. Previously this is needed because kernel builds doc tests with the default set of clippy flags; but now `clippy::disallowed_names` is globally allowed inside doc tests. Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260710-pin-init-sync-v1-3-8fa16cde87ae@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 7 ------- 1 file changed, 7 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index fd40c8f244a1..90e9d501d44a 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -70,7 +70,6 @@ //! that you need to write `<-` instead of `:` for fields that you want to initialize in-place. //! //! ```rust -//! # #![expect(clippy::disallowed_names)] //! # #![feature(allocator_api)] //! # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; //! # use core::pin::Pin; @@ -94,7 +93,6 @@ //! (or just the stack) to actually initialize a `Foo`: //! //! ```rust -//! # #![expect(clippy::disallowed_names)] //! # #![feature(allocator_api)] //! # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; //! # use core::{alloc::AllocError, pin::Pin}; @@ -456,7 +454,6 @@ pub use ::pin_init_internal::MaybeZeroable; /// # Examples /// /// ```rust -/// # #![expect(clippy::disallowed_names)] /// # #![feature(allocator_api)] /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; /// # use pin_init::*; @@ -508,7 +505,6 @@ macro_rules! stack_pin_init { /// # Examples /// /// ```rust -/// # #![expect(clippy::disallowed_names)] /// # #![feature(allocator_api)] /// # #[path = "../examples/error.rs"] mod error; use error::Error; /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; @@ -535,7 +531,6 @@ macro_rules! stack_pin_init { /// ``` /// /// ```rust -/// # #![expect(clippy::disallowed_names)] /// # #![feature(allocator_api)] /// # #[path = "../examples/error.rs"] mod error; use error::Error; /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; @@ -658,7 +653,6 @@ macro_rules! stack_try_pin_init { /// Users of `Foo` can now create it like this: /// /// ```rust -/// # #![expect(clippy::disallowed_names)] /// # use pin_init::*; /// # use core::pin::Pin; /// # #[pin_data] @@ -1031,7 +1025,6 @@ pub unsafe trait Init: PinInit { /// # Examples /// /// ```rust - /// # #![expect(clippy::disallowed_names)] /// use pin_init::{init, init_zeroed, Init}; /// /// struct Foo { -- cgit v1.2.3 From c1722ae6fefe3723f31656172f4bc196225506dc Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 10 Jul 2026 17:20:34 +0100 Subject: rust: pin-init: internal: remove `allow` and `expect`s that don't fire Most warnings are suppressed from external macro expansions by default. Thus remove `allow` and `expect`s for them. Note that `unfulfilled_lint_expectations` is one of them too. This means that all of our `expect`s inside macros do nothing, and actually mislead people to the lints would be actually emitted without them. Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260710-pin-init-sync-v1-4-8fa16cde87ae@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 2 +- rust/pin-init/internal/src/pin_data.rs | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index 28d30805d06b..c1197a994c82 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -334,7 +334,7 @@ fn make_field_check( }), }; quote! { - #[allow(unreachable_code, clippy::diverging_sub_expression)] + #[allow(unreachable_code)] // We use unreachable code to perform field checks. They're still checked by the compiler. // SAFETY: this code is never executed. let _ = || unsafe { diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 263f67300727..4438107682e0 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -245,7 +245,6 @@ fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenSt // `Drop`. Additionally we will implement this trait for the struct leading to a conflict, // if it also implements `Drop` trait MustNotImplDrop {} - #[expect(drop_bounds)] impl MustNotImplDrop for T {} impl #impl_generics MustNotImplDrop for #ident #ty_generics #whr @@ -253,7 +252,6 @@ fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenSt // We also take care to prevent users from writing a useless `PinnedDrop` implementation. // They might implement `PinnedDrop` correctly for the struct, but forget to give // `PinnedDrop` as the parameter to `#[pin_data]`. - #[expect(non_camel_case_types)] trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} impl UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} @@ -432,7 +430,6 @@ fn generate_the_pin_data( {} #[allow(dead_code)] // Some functions might never be used and private. - #[expect(clippy::missing_safety_doc)] impl #impl_generics __ThePinData #ty_generics #whr { -- cgit v1.2.3 From 751ecd5a19cf2c55807bf30f35bafc32e179a19c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 10 Jul 2026 17:20:35 +0100 Subject: rust: pin-init: internal: generate brace in macro for init code blocks `init!` support interleaving code execution and initialization, and code execution is done using `_: { ... }` syntax. If the code inside block is a single statement, Rust may add a lint about unused braces, but the suggestion will be incorrect as block is required by pin-init. Currently we use `unused_brace` to suppress this, but this affect everything nested inside as well. Use an alternative approach by generating the block from the macro, then rustc will know to not emit the lint. Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260710-pin-init-sync-v1-5-8fa16cde87ae@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index c1197a994c82..fd0b5ea4a0a3 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -233,10 +233,12 @@ fn init_fields( InitializerKind::Value { ident, .. } => ident, InitializerKind::Init { ident, .. } => ident, InitializerKind::Code { block, .. } => { + let stmt = &block.stmts; res.extend(quote! { #(#attrs)* - #[allow(unused_braces)] - #block + { + #(#stmt)* + } }); continue; } -- cgit v1.2.3 From 7e4d9c946de525cac36bb693c42a147b8e9f03c5 Mon Sep 17 00:00:00 2001 From: Mirko Adzic Date: Fri, 10 Jul 2026 17:20:36 +0100 Subject: rust: pin-init: make `[pin_]init_array_from_fn` unwind safe The previous code only ran cleanup on the explicit error path. If the per- element initializer panicked partway through, the elements already written into the array would be leaked: their `Drop` impls would never run. This violates the pinning requirement. Fix the unwind safety issue by adding a guard type that drops element on both error and panic path. To avoid having to duplicate code between `pin_init_array_from_fn` and the non-pin variant, extract the code to a shared `ArrayInit` type; this type is internal and not visible via API. Reported-by: Gary Guo Closes: https://github.com/Rust-for-Linux/pin-init/issues/136 Signed-off-by: Mirko Adzic Link: https://patch.msgid.link/20260710-pin-init-sync-v1-6-8fa16cde87ae@garyguo.net [ Split guard type and the initializer type, move the guard type to be within __pinned_init. - Gary ] Co-developed-by: Gary Guo Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 122 +++++++++++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 42 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 90e9d501d44a..3fc4a674a487 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1186,6 +1186,82 @@ pub fn uninit() -> impl Init, E> { unsafe { init_from_closure(|_| Ok(())) } } +/// Array initializer from element initializer. +struct ArrayInit(F, __internal::PhantomInvariant); + +// SAFETY: On success, all `N` elements of the array have been initialized. On error or panic, the +// elements that have been initialized so far are dropped, thus leaving the array uninitialized and +// ready to deallocate. +unsafe impl PinInit<[T; N], E> for ArrayInit +where + F: FnMut(usize) -> I, + I: PinInit, +{ + unsafe fn __pinned_init(mut self, slot: *mut [T; N]) -> Result<(), E> { + /// # Invariants + /// + /// - `ptr[..num_init]` contains initialized elements of type `T` + /// - `ptr[num_init..N]` (where N is the size of the array) contains uninitialized memory + struct ArrayInitGuard { + /// A pointer to the first element of the array. + ptr: *mut T, + /// The number of initialized elements in the array. + num_init: usize, + } + + impl Drop for ArrayInitGuard { + #[inline] + fn drop(&mut self) { + // SAFETY: Per type invariant, `self.ptr[..self.num_init]` are initialized. + unsafe { + core::ptr::drop_in_place(core::ptr::slice_from_raw_parts_mut( + self.ptr, + self.num_init, + )) + }; + } + } + + // INVARIANT: nothing is initialized yet. + let mut guard = ArrayInitGuard { + ptr: slot.cast::(), + num_init: 0, + }; + + for i in 0..N { + // INVARIANT: Elements `self.ptr[..self.num_init]` have been initialized + // thus far. This holds true for every `self.num_init = i`. + guard.num_init = i; + + let init = (self.0)(i); + // SAFETY: + // - The subslot is derived from `slot` with a valid offset. + // - If `Err` is touched, the subslot is not touched further, the guard will drop + // previously initialized elements only. + // - `slot` is pinned so is the subslot. + unsafe { init.__pinned_init(&raw mut (*slot)[i]) }?; + } + + // Dismiss the drop guard now that all elements are initialized. + core::mem::forget(guard); + Ok(()) + } +} + +// SAFETY: Follows the `PinInit` impl. `__init` executes the same code as `__pinned_init`. +unsafe impl Init<[T; N], E> for ArrayInit +where + F: FnMut(usize) -> I, + I: Init, +{ + #[inline(always)] + unsafe fn __init(self, slot: *mut [T; N]) -> Result<(), E> { + // SAFETY: `I: Init` cancels out the pinning requirement on subslots. The other safety + // requirements follow that of `__init`. + unsafe { self.__pinned_init(slot) } + } +} + /// Initializes an array by initializing each element via the provided initializer. /// /// # Examples @@ -1197,31 +1273,12 @@ pub fn uninit() -> impl Init, E> { /// assert_eq!(array.len(), 1_000); /// ``` pub fn init_array_from_fn( - mut make_init: impl FnMut(usize) -> I, + make_init: impl FnMut(usize) -> I, ) -> impl Init<[T; N], E> where I: Init, { - let init = move |slot: *mut [T; N]| { - let slot = slot.cast::(); - for i in 0..N { - let init = make_init(i); - // SAFETY: Since 0 <= `i` < N, it is still in bounds of `[T; N]`. - let ptr = unsafe { slot.add(i) }; - // SAFETY: The pointer is derived from `slot` and thus satisfies the `__init` - // requirements. - if let Err(e) = unsafe { init.__init(ptr) } { - // SAFETY: The loop has initialized the elements `slot[0..i]` and since we return - // `Err` below, `slot` will be considered uninitialized memory. - unsafe { ptr::drop_in_place(ptr::slice_from_raw_parts_mut(slot, i)) }; - return Err(e); - } - } - Ok(()) - }; - // SAFETY: The initializer above initializes every element of the array. On failure it drops - // any initialized elements and returns `Err`. - unsafe { init_from_closure(init) } + ArrayInit(make_init, __internal::PhantomInvariant::new()) } /// Initializes an array by initializing each element via the provided initializer. @@ -1240,31 +1297,12 @@ where /// assert_eq!(array.len(), 1_000); /// ``` pub fn pin_init_array_from_fn( - mut make_init: impl FnMut(usize) -> I, + make_init: impl FnMut(usize) -> I, ) -> impl PinInit<[T; N], E> where I: PinInit, { - let init = move |slot: *mut [T; N]| { - let slot = slot.cast::(); - for i in 0..N { - let init = make_init(i); - // SAFETY: Since 0 <= `i` < N, it is still in bounds of `[T; N]`. - let ptr = unsafe { slot.add(i) }; - // SAFETY: The pointer is derived from `slot` and thus satisfies the `__init` - // requirements. - if let Err(e) = unsafe { init.__pinned_init(ptr) } { - // SAFETY: The loop has initialized the elements `slot[0..i]` and since we return - // `Err` below, `slot` will be considered uninitialized memory. - unsafe { ptr::drop_in_place(ptr::slice_from_raw_parts_mut(slot, i)) }; - return Err(e); - } - } - Ok(()) - }; - // SAFETY: The initializer above initializes every element of the array. On failure it drops - // any initialized elements and returns `Err`. - unsafe { pin_init_from_closure(init) } + ArrayInit(make_init, __internal::PhantomInvariant::new()) } /// Construct an initializer in a closure and run it. -- cgit v1.2.3 From 0c20f77a26b89bc911d31cd79f1abe1a7ae57f60 Mon Sep 17 00:00:00 2001 From: Mirko Adzic Date: Fri, 10 Jul 2026 17:20:37 +0100 Subject: rust: pin-init: make `[pin_]chain` unwind safe Add a drop guard before the call to the chained closure so that the value initialized by the first stage is dropped if the closure errors or panics; `mem::forget` the guard on success. The previous code only ran cleanup on the explicit error path, leaking the first-stage value if the chained closure panicked. Reported-by: Gary Guo Closes: https://github.com/Rust-for-Linux/pin-init/issues/136 Suggested-by: Gary Guo Signed-off-by: Mirko Adzic Link: https://patch.msgid.link/20260710-pin-init-sync-v1-7-8fa16cde87ae@garyguo.net [ Fix Clippy missing safety comment false positive when `slot` and `guard` creation are merged in a single line. - Gary ] Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 3fc4a674a487..ef9f20b11034 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -959,13 +959,11 @@ where { unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { // SAFETY: All requirements fulfilled since this function is `__pinned_init`. - unsafe { self.0.__pinned_init(slot)? }; - // SAFETY: The above call initialized `slot` and we still have unique access. - let val = unsafe { &mut *slot }; - // SAFETY: `slot` is considered pinned. - let val = unsafe { Pin::new_unchecked(val) }; - // SAFETY: `slot` was initialized above. - (self.1)(val).inspect_err(|_| unsafe { core::ptr::drop_in_place(slot) }) + let slot = unsafe { __internal::Slot::<__internal::Pinned, _>::new(slot) }; + let mut guard = slot.init(self.0)?; + (self.1)(guard.let_binding())?; + core::mem::forget(guard); + Ok(()) } } @@ -1065,11 +1063,11 @@ where { unsafe fn __init(self, slot: *mut T) -> Result<(), E> { // SAFETY: All requirements fulfilled since this function is `__init`. - unsafe { self.0.__pinned_init(slot)? }; - // SAFETY: The above call initialized `slot` and we still have unique access. - (self.1)(unsafe { &mut *slot }).inspect_err(|_| - // SAFETY: `slot` was initialized above. - unsafe { core::ptr::drop_in_place(slot) }) + let slot = unsafe { __internal::Slot::<__internal::Unpinned, _>::new(slot) }; + let mut guard = slot.init(self.0)?; + (self.1)(guard.let_binding())?; + core::mem::forget(guard); + Ok(()) } } -- cgit v1.2.3 From 5bbf2b2deb94d0ef8324866d39cb5e0947ca5068 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 23 Jul 2026 19:19:44 +0100 Subject: rust: pin-init: internal: rework how `#[pin_data]` handles cfg Attribute macros are invoked without cfg being resolved. This adds quite a bit complexity to the macro because all of the macro needs to be careful to attach necessary cfgs. This becomes especially tricky for tuple structs. Thus, it is convenient if cfgs are all resolved like derive macros. The most optimal way to handle this is via `TokenStream::expand_expr`, but that is still unstable. We can also create an internal derive macro and transform the attribute macro invocation to be derive macro, but doing requires us to serialize all extracted information in a form of helper attributes; it would also make it more difficult if we want to make changes to the struct (which the self-reference feature would need). Implement an approach where we generate two cfg-gated macro invocations with cfg resolved within the invocation. This would mean when the loop falls through, all field cfgs are resolved, so remove all handling of cfg_attrs for the rest of the macro. Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 82 ++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 18 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 4438107682e0..3c9d9c7364e2 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -1,13 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, ToTokens}; use syn::{ parse::{End, Nothing, Parse}, parse_quote, parse_quote_spanned, spanned::Spanned, visit_mut::VisitMut, - Attribute, Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause, + Field, Fields, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause, }; use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; @@ -35,10 +35,18 @@ impl Parse for Args { } } +impl ToTokens for Args { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Nothing(_) => (), + Self::PinnedDrop(kw) => kw.to_tokens(tokens), + } + } +} + struct FieldInfo<'a> { field: &'a Field, pinned: bool, - cfg_attrs: Vec<&'a Attribute>, } pub(crate) fn pin_data( @@ -68,6 +76,55 @@ pub(crate) fn pin_data( } }; + // Handling cfg can gets very complicated, especially for tuple structs. Therefore, resolve all + // field cfgs first before continuing. + // + // We need to perform this after parsing so we can reliably detect field cfgs. + for (field_idx, field) in struct_.fields.iter_mut().enumerate() { + let cfg: Vec<_> = field + .attrs + .iter() + .filter(|a| a.path().is_ident("cfg")) + .map(|a| { + a.parse_args::() + .expect("parse as token stream cannot fail") + }) + .collect(); + + if cfg.is_empty() { + continue; + } + + field.attrs.retain(|a| !a.path().is_ident("cfg")); + let cfg_true_struct = quote!(#struct_); + + let punctuated = match &mut struct_.fields { + Fields::Named(fields) => &mut fields.named, + Fields::Unnamed(fields) => &mut fields.unnamed, + Fields::Unit => unreachable!(), + }; + *punctuated = std::mem::take(punctuated) + .into_pairs() + .enumerate() + .filter(|&(i, _)| i != field_idx) + .map(|(_, p)| p) + .collect(); + let cfg_false_struct = quote!(#struct_); + + // Resolve one field at a time until we've got no more field cfgs. + // + // This is linear time because macro invocations with false cfg will not be expanded. + return Ok(quote!( + #[cfg(all(#(#cfg,)*))] + #[::pin_init::pin_data(#args)] + #cfg_true_struct + + #[cfg(not(all(#(#cfg,)*)))] + #[::pin_init::pin_data(#args)] + #cfg_false_struct + )); + } + // The generics might contain the `Self` type. Since this macro will define a new type with the // same generics and bounds, this poses a problem: `Self` will refer to the new type as opposed // to this struct definition. Therefore we have to replace `Self` with the concrete name. @@ -90,16 +147,14 @@ pub(crate) fn pin_data( dcx.error(&field, "#[pin] attribute specified more than once"); } - let cfg_attrs = field - .attrs - .iter() - .filter(|a| a.path().is_ident("cfg")) - .collect(); + assert!( + !field.attrs.iter().any(|a| a.path().is_ident("cfg")), + "cfgs should be all resolved at this point" + ); FieldInfo { field: &*field, pinned: pinned_count != 0, - cfg_attrs, } }) .collect(); @@ -185,9 +240,7 @@ fn generate_unpin_impl( let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| { let ident = f.field.ident.as_ref().unwrap(); let ty = &f.field.ty; - let cfg_attrs = &f.cfg_attrs; quote!( - #(#cfg_attrs)* #ident: #ty ) }); @@ -280,7 +333,6 @@ fn generate_projections( .iter() .map(|field| { let Field { vis, ident, ty, .. } = &field.field; - let cfg_attrs = &field.cfg_attrs; let ident = ident .as_ref() @@ -288,11 +340,9 @@ fn generate_projections( if field.pinned { ( quote!( - #(#cfg_attrs)* #vis #ident: ::core::pin::Pin<&'__pin mut #ty>, ), quote!( - #(#cfg_attrs)* // SAFETY: this field is structurally pinned. #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) }, ), @@ -300,11 +350,9 @@ fn generate_projections( } else { ( quote!( - #(#cfg_attrs)* #vis #ident: &'__pin mut #ty, ), quote!( - #(#cfg_attrs)* #ident: &mut #this.#ident, ), ) @@ -374,7 +422,6 @@ fn generate_the_pin_data( .iter() .map(|f| { let Field { vis, ident, ty, .. } = f.field; - let cfg_attrs = &f.cfg_attrs; let field_name = ident .as_ref() @@ -391,7 +438,6 @@ fn generate_the_pin_data( /// - `(*slot).#field_name` is properly aligned. /// - `(*slot).#field_name` points to uninitialized and exclusively accessed /// memory. - #(#cfg_attrs)* // Allow `non_snake_case` since the same warning will be emitted on // the struct definition. #[allow(non_snake_case)] -- cgit v1.2.3 From 9e813b9abdfb626f0607b1ec94d201a2b8691f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Antinori?= Date: Thu, 23 Jul 2026 19:19:45 +0100 Subject: rust: pin-init: docs: link `Zeroable::zeroed` and `pin_init::zeroed` in documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify the comments in the `pin_init::zeroed` and `Zeroable::zeroed` functions to cross-reference each other and make developers aware of both options. This also adapts the example code in `Zeroable::zeroed` doc comments to use that function. Suggested-by: Miguel Ojeda Link: https://lore.kernel.org/rust-for-linux/CANiq72kdCAyRUmXFcqQfkHpk1miG8Gagsn0_5U8p4WpKxv9d_g@mail.gmail.com/ Signed-off-by: Nicolás Antinori [ Fix link. - Gary ] Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index ef9f20b11034..1f5005c110cd 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1539,10 +1539,13 @@ pub unsafe trait Zeroable { /// Whenever a type implements [`Zeroable`], this function should be preferred over /// [`core::mem::zeroed()`] or using `MaybeUninit::zeroed().assume_init()`. /// + /// As const traits are not yet stable, [`pin_init::zeroed()`] can be used instead + /// when initialization is required in a `const` context. + /// /// # Examples /// /// ``` - /// use pin_init::{Zeroable, zeroed}; + /// use pin_init::Zeroable; /// /// #[derive(Zeroable)] /// struct Point { @@ -1550,7 +1553,7 @@ pub unsafe trait Zeroable { /// y: u32, /// } /// - /// let point: Point = zeroed(); + /// let point: Point = Zeroable::zeroed(); /// assert_eq!(point.x, 0); /// assert_eq!(point.y, 0); /// ``` @@ -1582,6 +1585,9 @@ pub fn init_zeroed() -> impl Init { /// Whenever a type implements [`Zeroable`], this function should be preferred over /// [`core::mem::zeroed()`] or using `MaybeUninit::zeroed().assume_init()`. /// +/// While const traits remain unstable, this function serves as the `const` version of +/// [`Zeroable::zeroed()`]. +/// /// # Examples /// /// ``` -- cgit v1.2.3 From 6d0795b507fb1db2e6aefe533d949db3a4abf4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Antinori?= Date: Thu, 23 Jul 2026 19:19:46 +0100 Subject: rust: pin-init: mark `pin_init::zeroed` and `Zeroable::zeroed` as `#[inline]` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `pin_init::zeroed` function is a trivial wrapper around `unsafe { core::mem::zeroed() }`, whereas `Zeroable::zeroed` is a trivial wrapper around `pin_init::zeroed`. Mark them both as `#[inline]` to avoid generating unnecessary symbols for them. Signed-off-by: Nicolás Antinori Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'rust/pin-init') diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 1f5005c110cd..f4ccb0e87200 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1557,6 +1557,7 @@ pub unsafe trait Zeroable { /// assert_eq!(point.x, 0); /// assert_eq!(point.y, 0); /// ``` + #[inline] fn zeroed() -> Self where Self: Sized, @@ -1603,6 +1604,7 @@ pub fn init_zeroed() -> impl Init { /// assert_eq!(point.x, 0); /// assert_eq!(point.y, 0); /// ``` +#[inline] pub const fn zeroed() -> T { // SAFETY:By the type invariants of `Zeroable`, all zeroes is a valid bit pattern for `T`. unsafe { core::mem::zeroed() } -- cgit v1.2.3 From c998b661b7c024dfd6dd893927506e32ee8a42c5 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 29 Jul 2026 16:38:43 +0100 Subject: rust: pin-init: examples: use `Wrapper::pin_init` instead of manual reimplementation `UnsafeCell` gains the method via the extension trait `Wrapper`. Link: https://patch.msgid.link/20260729-merge-init-v2-1-26adf47109e7@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/examples/mutex.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/examples/mutex.rs b/rust/pin-init/examples/mutex.rs index 882f3e23f5dd..e8d4dbb664fe 100644 --- a/rust/pin-init/examples/mutex.rs +++ b/rust/pin-init/examples/mutex.rs @@ -79,11 +79,7 @@ impl CMutex { wait_list <- ListHead::new(), spin_lock: SpinLock::new(), locked: Cell::new(false), - data <- unsafe { - pin_init_from_closure(|slot: *mut UnsafeCell| { - val.__pinned_init(slot.cast::()) - }) - }, + data <- UnsafeCell::pin_init(val), }) } -- cgit v1.2.3 From 91665820d9bf511e0c3fdf3edb464ba23130dafe Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 29 Jul 2026 16:38:44 +0100 Subject: rust: pin-init: merge `__pinned_init` and `__init` These functions have the same requirements and are also required to execute the same code. Prevent duplication by merging them to the single function and document the additional relaxation of `Init::__init` on both the merged function and the safety requirement of `Init`. The existing `__pinned_init` function is deprecated and kept for compatibility for existing users. For `cfg(kernel)`, it is soft-deprecated for now and will be removed when all users are migrated. Link: https://patch.msgid.link/20260729-merge-init-v2-2-26adf47109e7@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/examples/static_init.rs | 9 +-- rust/pin-init/src/__internal.rs | 8 +- rust/pin-init/src/alloc.rs | 6 +- rust/pin-init/src/lib.rs | 146 +++++++++++++--------------------- 4 files changed, 67 insertions(+), 102 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/examples/static_init.rs b/rust/pin-init/examples/static_init.rs index 58cd4241b78c..8e71556ffe85 100644 --- a/rust/pin-init/examples/static_init.rs +++ b/rust/pin-init/examples/static_init.rs @@ -59,7 +59,7 @@ impl> ops::Deref for StaticInit { println!("doing init"); let ptr = self.cell.get().cast::(); match self.init.take() { - Some(f) => unsafe { f.__pinned_init(ptr).unwrap() }, + Some(f) => unsafe { f.__init(ptr).unwrap() }, None => unsafe { core::hint::unreachable_unchecked() }, } self.present.set(true); @@ -71,13 +71,10 @@ impl> ops::Deref for StaticInit { pub struct CountInit; unsafe impl PinInit> for CountInit { - unsafe fn __pinned_init( - self, - slot: *mut CMutex, - ) -> Result<(), core::convert::Infallible> { + unsafe fn __init(self, slot: *mut CMutex) -> Result<(), core::convert::Infallible> { let init = CMutex::new(0); std::thread::sleep(std::time::Duration::from_millis(1000)); - unsafe { init.__pinned_init(slot) } + unsafe { init.__init(slot) } } } diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index 56dc655e323e..ae9a0e68cd75 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -181,7 +181,7 @@ impl StackInit { unsafe { this.value.assume_init_drop() }; } // SAFETY: The memory slot is valid and this type ensures that it will stay pinned. - unsafe { init.__pinned_init(this.value.as_mut_ptr())? }; + unsafe { init.__init(this.value.as_mut_ptr())? }; // INVARIANT: `this.value` is initialized above. this.is_init = true; // SAFETY: The slot is now pinned, since we will never give access to `&mut T`. @@ -289,7 +289,7 @@ impl Slot { // - when `Err` is returned, we also propagate the error without touching `ptr`; // also `self` is consumed so it cannot be touched further. // - the drop guard will not hand out `&mut` (only `Pin<&mut T>`). - unsafe { init.__pinned_init(self.ptr)? }; + unsafe { init.__init(self.ptr)? }; // SAFETY: // - `self.ptr` is valid, properly aligned and pinned per type invariant. @@ -396,9 +396,9 @@ impl Default for AlwaysFail { } } -// SAFETY: `__pinned_init` always fails, which is always okay. +// SAFETY: `__init` always fails, which is always okay. unsafe impl PinInit for AlwaysFail { - unsafe fn __pinned_init(self, _slot: *mut T) -> Result<(), ()> { + unsafe fn __init(self, _slot: *mut T) -> Result<(), ()> { Err(()) } } diff --git a/rust/pin-init/src/alloc.rs b/rust/pin-init/src/alloc.rs index 5017f57442d8..641f4c7ce890 100644 --- a/rust/pin-init/src/alloc.rs +++ b/rust/pin-init/src/alloc.rs @@ -38,7 +38,7 @@ pub trait InPlaceInit: Sized { fn pin_init(init: impl PinInit) -> Result, AllocError> { // SAFETY: We delegate to `init` and only change the error type. let init = unsafe { - pin_init_from_closure(|slot| match init.__pinned_init(slot) { + pin_init_from_closure(|slot| match init.__init(slot) { Ok(()) => Ok(()), Err(i) => match i {}, }) @@ -109,7 +109,7 @@ impl InPlaceInit for Arc { let slot = slot.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid and will not be moved, because we pin it later. - unsafe { init.__pinned_init(slot)? }; + unsafe { init.__init(slot)? }; // SAFETY: All fields have been initialized and this is the only `Arc` to that data. Ok(unsafe { Pin::new_unchecked(this.assume_init()) }) } @@ -149,7 +149,7 @@ impl InPlaceWrite for Box> { let slot = self.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid and will not be moved, because we pin it later. - unsafe { init.__pinned_init(slot)? }; + unsafe { init.__init(slot)? }; // SAFETY: All fields have been initialized. Ok(unsafe { self.assume_init() }.into()) } diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index f4ccb0e87200..fde53473763f 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -889,7 +889,7 @@ macro_rules! assert_pinned { /// When implementing this trait you will need to take great care. Also there are probably very few /// cases where a manual implementation is necessary. Use [`pin_init_from_closure`] where possible. /// -/// The [`PinInit::__pinned_init`] function: +/// The [`PinInit::__init`] function: /// - returns `Ok(())` if it initialized every field of `slot`, /// - returns `Err(err)` if it encountered an error and then cleaned `slot`, this means: /// - `slot` can be deallocated without UB occurring, @@ -909,6 +909,20 @@ macro_rules! assert_pinned { #[cfg_attr(not(kernel), doc = "[`Box`]: alloc::alloc::boxed::Box")] #[must_use = "An initializer must be used in order to create its value."] pub unsafe trait PinInit: Sized { + /// Alias of [`PinInit::__init`]. + /// + /// New code should use `__init` instead. + /// + /// # Safety + /// + /// Same as `__init`. + #[inline(always)] + #[cfg_attr(not(kernel), deprecated = "use `__init` instead")] + unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { + // SAFETY: Per safety requirement. + unsafe { self.__init(slot) } + } + /// Initializes `slot`. /// /// # Safety @@ -917,7 +931,8 @@ pub unsafe trait PinInit: Sized { /// - the caller does not touch `slot` when `Err` is returned, they are only permitted to /// deallocate. /// - `slot` will not move until it is dropped, i.e. it will be pinned. - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E>; + /// If `Self: Init`, this requirement is cancelled and it may be moved. + unsafe fn __init(self, slot: *mut T) -> Result<(), E>; /// First initializes the value using `self` then calls the function `f` with the initialized /// value. @@ -948,7 +963,7 @@ pub unsafe trait PinInit: Sized { /// An initializer returned by [`PinInit::pin_chain`]. pub struct ChainPinInit(I, F, __internal::PhantomInvariant<(E, T)>); -// SAFETY: The `__pinned_init` function is implemented such that it +// SAFETY: The `__init` function is implemented such that it // - returns `Ok(())` on successful initialization, // - returns `Err(err)` on error and in this case `slot` will be dropped. // - considers `slot` pinned. @@ -957,8 +972,8 @@ where I: PinInit, F: FnOnce(Pin<&mut T>) -> Result<(), E>, { - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { - // SAFETY: All requirements fulfilled since this function is `__pinned_init`. + unsafe fn __init(self, slot: *mut T) -> Result<(), E> { + // SAFETY: All requirements fulfilled since this function is `__init`. let slot = unsafe { __internal::Slot::<__internal::Pinned, _>::new(slot) }; let mut guard = slot.init(self.0)?; (self.1)(guard.let_binding())?; @@ -980,19 +995,8 @@ where /// When implementing this trait you will need to take great care. Also there are probably very few /// cases where a manual implementation is necessary. Use [`init_from_closure`] where possible. /// -/// The [`Init::__init`] function: -/// - returns `Ok(())` if it initialized every field of `slot`, -/// - returns `Err(err)` if it encountered an error and then cleaned `slot`, this means: -/// - `slot` can be deallocated without UB occurring, -/// - `slot` does not need to be dropped, -/// - `slot` is not partially initialized. -/// - while constructing the `T` at `slot` it upholds the pinning invariants of `T`. -/// -/// The `__pinned_init` function from the supertrait [`PinInit`] needs to execute the exact same -/// code as `__init`. -/// -/// Contrary to its supertype [`PinInit`] the caller is allowed to -/// move the pointee after initialization. +/// The [`PinInit::__init`] function must work without the pinning requirement; the caller is +/// allowed to move the pointee after initialization. /// #[cfg_attr( kernel, @@ -1006,15 +1010,6 @@ where #[cfg_attr(not(kernel), doc = "[`Box`]: alloc::alloc::boxed::Box")] #[must_use = "An initializer must be used in order to create its value."] pub unsafe trait Init: PinInit { - /// Initializes `slot`. - /// - /// # Safety - /// - /// - `slot` is a valid pointer to uninitialized memory. - /// - the caller does not touch `slot` when `Err` is returned, they are only permitted to - /// deallocate. - unsafe fn __init(self, slot: *mut T) -> Result<(), E>; - /// First initializes the value using `self` then calls the function `f` with the initialized /// value. /// @@ -1053,10 +1048,18 @@ pub unsafe trait Init: PinInit { /// An initializer returned by [`Init::chain`]. pub struct ChainInit(I, F, __internal::PhantomInvariant<(E, T)>); +// SAFETY: The `__init` function does not rely on the pinning requirement. +unsafe impl Init for ChainInit +where + I: Init, + F: FnOnce(&mut T) -> Result<(), E>, +{ +} + // SAFETY: The `__init` function is implemented such that it // - returns `Ok(())` on successful initialization, // - returns `Err(err)` on error and in this case `slot` will be dropped. -unsafe impl Init for ChainInit +unsafe impl PinInit for ChainInit where I: Init, F: FnOnce(&mut T) -> Result<(), E>, @@ -1071,44 +1074,28 @@ where } } -// SAFETY: `__pinned_init` behaves exactly the same as `__init`. -unsafe impl PinInit for ChainInit -where - I: Init, - F: FnOnce(&mut T) -> Result<(), E>, -{ - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { - // SAFETY: `__init` has less strict requirements compared to `__pinned_init`. - unsafe { self.__init(slot) } - } -} - /// Implement `PinInit` and `Init` for closures. /// /// It is unsafe to create this type, since the closure needs to fulfill the same safety -/// requirement as the `__pinned_init`/`__init` functions. +/// requirement as the `__init` functions. struct InitClosure(F, __internal::PhantomInvariant); -// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the -// `__init` invariants. -unsafe impl Init for InitClosure -where - F: FnOnce(*mut T) -> Result<(), E>, +// SAFETY: When constructing via `init_from_closure`, the `__init` function does not rely on the +// pinning requirement. When constructing via `pin_init_from_closure`, the opaque type prevents this +// implementation from being visible. +unsafe impl Init for InitClosure where + F: FnOnce(*mut T) -> Result<(), E> { - #[inline] - unsafe fn __init(self, slot: *mut T) -> Result<(), E> { - (self.0)(slot) - } } // SAFETY: While constructing the `InitClosure`, the user promised that it upholds the -// `__pinned_init` invariants. +// `__init` invariants. unsafe impl PinInit for InitClosure where F: FnOnce(*mut T) -> Result<(), E>, { #[inline] - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { + unsafe fn __init(self, slot: *mut T) -> Result<(), E> { (self.0)(slot) } } @@ -1160,7 +1147,7 @@ pub const unsafe fn init_from_closure( pub const unsafe fn cast_pin_init(init: impl PinInit) -> impl PinInit { // SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety // requirements. - unsafe { pin_init_from_closure(|ptr: *mut U| init.__pinned_init(ptr.cast::())) } + unsafe { pin_init_from_closure(|ptr: *mut U| init.__init(ptr.cast::())) } } /// Changes the to be initialized type. @@ -1195,7 +1182,7 @@ where F: FnMut(usize) -> I, I: PinInit, { - unsafe fn __pinned_init(mut self, slot: *mut [T; N]) -> Result<(), E> { + unsafe fn __init(mut self, slot: *mut [T; N]) -> Result<(), E> { /// # Invariants /// /// - `ptr[..num_init]` contains initialized elements of type `T` @@ -1237,7 +1224,7 @@ where // - If `Err` is touched, the subslot is not touched further, the guard will drop // previously initialized elements only. // - `slot` is pinned so is the subslot. - unsafe { init.__pinned_init(&raw mut (*slot)[i]) }?; + unsafe { init.__init(&raw mut (*slot)[i]) }?; } // Dismiss the drop guard now that all elements are initialized. @@ -1246,18 +1233,13 @@ where } } -// SAFETY: Follows the `PinInit` impl. `__init` executes the same code as `__pinned_init`. +// SAFETY: `I: Init` cancels out the pinning requirement on subslots, which is the only place in the +// `__init` function that relies on `slot` being pinned. unsafe impl Init<[T; N], E> for ArrayInit where F: FnMut(usize) -> I, I: Init, { - #[inline(always)] - unsafe fn __init(self, slot: *mut [T; N]) -> Result<(), E> { - // SAFETY: `I: Init` cancels out the pinning requirement on subslots. The other safety - // requirements follow that of `__init`. - unsafe { self.__pinned_init(slot) } - } } /// Initializes an array by initializing each element via the provided initializer. @@ -1336,13 +1318,13 @@ where { // SAFETY: // - If `make_init` returns `Err`, `Err` is returned and `slot` is completely uninitialized, - // - If `make_init` returns `Ok`, safety requirement are fulfilled by `init.__pinned_init`. - // - The safety requirements of `init.__pinned_init` are fulfilled, since it's being called - // from an initializer. + // - If `make_init` returns `Ok`, safety requirement are fulfilled by `init.__init`. + // - The safety requirements of `init.__init` are fulfilled, since it's being called from an + // initializer. unsafe { pin_init_from_closure(move |slot: *mut T| -> Result<(), E> { let init = make_init()?; - init.__pinned_init(slot) + init.__init(slot) }) } } @@ -1390,41 +1372,27 @@ where } } -// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of `slot`. -unsafe impl Init for T { - unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> { - // SAFETY: `slot` is valid for writes by the safety requirements of this function. - unsafe { slot.write(self) }; - Ok(()) - } -} +// SAFETY: The `__init` function does not rely on slot being pinned after it returns. +unsafe impl Init for T {} -// SAFETY: the `__pinned_init` function always returns `Ok(())` and initializes every field of +// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of // `slot`. Additionally, all pinning invariants of `T` are upheld. unsafe impl PinInit for T { - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), Infallible> { + unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> { // SAFETY: `slot` is valid for writes by the safety requirements of this function. unsafe { slot.write(self) }; Ok(()) } } -// SAFETY: when the `__init` function returns with -// - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld. -// - `Err(err)`, slot was not written to. -unsafe impl Init for Result { - unsafe fn __init(self, slot: *mut T) -> Result<(), E> { - // SAFETY: `slot` is valid for writes by the safety requirements of this function. - unsafe { slot.write(self?) }; - Ok(()) - } -} +// SAFETY: The `__init` function does not rely on slot being pinned after it returns. +unsafe impl Init for Result {} -// SAFETY: when the `__pinned_init` function returns with +// SAFETY: when the `__init` function returns with // - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld. // - `Err(err)`, slot was not written to. unsafe impl PinInit for Result { - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { + unsafe fn __init(self, slot: *mut T) -> Result<(), E> { // SAFETY: `slot` is valid for writes by the safety requirements of this function. unsafe { slot.write(self?) }; Ok(()) @@ -1467,7 +1435,7 @@ impl InPlaceWrite for &'static mut MaybeUninit { // // The `'static` borrow guarantees the data will not be // moved/invalidated until it gets dropped (which is never). - unsafe { init.__pinned_init(slot)? }; + unsafe { init.__init(slot)? }; // SAFETY: The above call initialized the memory. Ok(Pin::static_mut(unsafe { self.assume_init_mut() })) -- cgit v1.2.3 From d5492db2bf30b7e617e41d4fe3dba22475a1a354 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 29 Jul 2026 16:38:45 +0100 Subject: rust: pin-init: add `raw_init` and `raw_try_init` and recommend over `__init` The `__init` method is not designed to be a public API (existence of "__" is a hint for this); but currently there is no other API that allows raw initialization on pointers. Add `raw_init` and `raw_try_init` and recommend people to use this instead if raw pointer initialization is needed. Link: https://patch.msgid.link/20260729-merge-init-v2-3-26adf47109e7@garyguo.net [ Renamed from `ptr_[try_]init` to `raw_[try_]init`. - Gary ] Reviewed-by: Benno Lossin Signed-off-by: Gary Guo --- rust/pin-init/examples/static_init.rs | 5 +++-- rust/pin-init/src/lib.rs | 32 +++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/examples/static_init.rs b/rust/pin-init/examples/static_init.rs index 8e71556ffe85..8dd52313c1b8 100644 --- a/rust/pin-init/examples/static_init.rs +++ b/rust/pin-init/examples/static_init.rs @@ -59,7 +59,7 @@ impl> ops::Deref for StaticInit { println!("doing init"); let ptr = self.cell.get().cast::(); match self.init.take() { - Some(f) => unsafe { f.__init(ptr).unwrap() }, + Some(f) => unsafe { pin_init::raw_init(ptr, f) }, None => unsafe { core::hint::unreachable_unchecked() }, } self.present.set(true); @@ -74,7 +74,8 @@ unsafe impl PinInit> for CountInit { unsafe fn __init(self, slot: *mut CMutex) -> Result<(), core::convert::Infallible> { let init = CMutex::new(0); std::thread::sleep(std::time::Duration::from_millis(1000)); - unsafe { init.__init(slot) } + unsafe { pin_init::raw_init(slot, init) }; + Ok(()) } } diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index fde53473763f..97eaef6f2958 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -917,7 +917,7 @@ pub unsafe trait PinInit: Sized { /// /// Same as `__init`. #[inline(always)] - #[cfg_attr(not(kernel), deprecated = "use `__init` instead")] + #[cfg_attr(not(kernel), deprecated = "use `raw_try_init` instead")] unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { // SAFETY: Per safety requirement. unsafe { self.__init(slot) } @@ -925,6 +925,8 @@ pub unsafe trait PinInit: Sized { /// Initializes `slot`. /// + /// It is not recommended to call this directly. Use [`raw_init`] or [`raw_try_init`]. + /// /// # Safety /// /// - `slot` is a valid pointer to uninitialized memory. @@ -960,6 +962,34 @@ pub unsafe trait PinInit: Sized { } } +/// Initializes `slot` with an initializer. +/// +/// # Safety +/// +/// - `slot` is a valid pointer to uninitialized memory. +/// - `slot` will not move until it is dropped, i.e. it will be pinned. +/// If `init` implements `Init`, this requirement is cancelled and it may be moved. +#[inline(always)] +pub unsafe fn raw_init(slot: *mut T, init: impl PinInit) { + // SAFETY: Per safety requirement. + unsafe { init.__init(slot).unwrap_or_else(|e| match e {}) } +} + +/// Fallibly initializes `slot` with an initializer. +/// +/// # Safety +/// +/// - `slot` is a valid pointer to uninitialized memory. +/// - the caller does not touch `slot` when `Err` is returned, they are only permitted to +/// deallocate. +/// - `slot` will not move until it is dropped, i.e. it will be pinned. +/// If `init` implements `Init`, this requirement is cancelled and it may be moved. +#[inline(always)] +pub unsafe fn raw_try_init(slot: *mut T, init: impl PinInit) -> Result<(), E> { + // SAFETY: Per safety requirement. + unsafe { init.__init(slot) } +} + /// An initializer returned by [`PinInit::pin_chain`]. pub struct ChainPinInit(I, F, __internal::PhantomInvariant<(E, T)>); -- cgit v1.2.3 From 1f7fa1374d3bb455944128fe5c30c19f8d3501c7 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 29 Jul 2026 16:38:47 +0100 Subject: rust: pin-init: remove `__pinned_init` method for `cfg(kernel)` Remove `__pinned_init` for kernel configuration, with all users gone. Still perserve it temporarily as deprecated so other users have time to move off it. Link: https://patch.msgid.link/20260729-merge-init-v2-5-26adf47109e7@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'rust/pin-init') diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 97eaef6f2958..6e9eb90db52c 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -917,7 +917,8 @@ pub unsafe trait PinInit: Sized { /// /// Same as `__init`. #[inline(always)] - #[cfg_attr(not(kernel), deprecated = "use `raw_try_init` instead")] + #[cfg(not(kernel))] + #[deprecated = "use `raw_try_init` instead"] unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { // SAFETY: Per safety requirement. unsafe { self.__init(slot) } -- cgit v1.2.3 From 1e26aea0355ad2afa1ccbc62885c01f5bcfc58ca Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 3 Aug 2026 14:02:01 +0100 Subject: rust: pin-init: add `#[inline]` to small functions Currently `pin-init` crate is missing many inline annotations. They are all generic so still get inlined in normal builds, but are not inlined in `-C opt-level=s` build. Mark these functions as `#[inline]` so they are considered for inlining regardless. Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 2 ++ rust/pin-init/src/__internal.rs | 5 +++++ rust/pin-init/src/alloc.rs | 4 ++++ rust/pin-init/src/lib.rs | 17 +++++++++++++++++ 4 files changed, 28 insertions(+) (limited to 'rust/pin-init') diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 3c9d9c7364e2..ff194d27565e 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -468,6 +468,7 @@ fn generate_the_pin_data( impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics #whr { + #[inline] fn clone(&self) -> Self { *self } } @@ -499,6 +500,7 @@ fn generate_the_pin_data( { type PinData = __ThePinData #ty_generics; + #[inline] unsafe fn __pin_data() -> Self::PinData { __ThePinData { __phantom: ::pin_init::__internal::PhantomInvariant::new() } } diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index ae9a0e68cd75..8e9fd18b993f 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -105,6 +105,7 @@ pub unsafe trait HasInitData { pub struct AllData(PhantomInvariant); impl Clone for AllData { + #[inline] fn clone(&self) -> Self { *self } @@ -127,6 +128,7 @@ impl AllData { unsafe impl HasInitData for T { type InitData = AllData; + #[inline] unsafe fn __init_data() -> Self::InitData { AllData(PhantomInvariant::new()) } @@ -385,12 +387,14 @@ pub struct AlwaysFail { impl AlwaysFail { /// Creates a new initializer that always fails. + #[inline] pub fn new() -> Self { Self { _t: PhantomData } } } impl Default for AlwaysFail { + #[inline] fn default() -> Self { Self::new() } @@ -398,6 +402,7 @@ impl Default for AlwaysFail { // SAFETY: `__init` always fails, which is always okay. unsafe impl PinInit for AlwaysFail { + #[inline] unsafe fn __init(self, _slot: *mut T) -> Result<(), ()> { Err(()) } diff --git a/rust/pin-init/src/alloc.rs b/rust/pin-init/src/alloc.rs index 641f4c7ce890..471652e8663a 100644 --- a/rust/pin-init/src/alloc.rs +++ b/rust/pin-init/src/alloc.rs @@ -35,6 +35,7 @@ pub trait InPlaceInit: Sized { /// type. /// /// If `T: !Unpin` it will not be able to move afterwards. + #[inline] fn pin_init(init: impl PinInit) -> Result, AllocError> { // SAFETY: We delegate to `init` and only change the error type. let init = unsafe { @@ -52,6 +53,7 @@ pub trait InPlaceInit: Sized { E: From; /// Use the given initializer to in-place initialize a `T`. + #[inline] fn init(init: impl Init) -> Result { // SAFETY: We delegate to `init` and only change the error type. let init = unsafe { @@ -136,6 +138,7 @@ impl InPlaceInit for Arc { impl InPlaceWrite for Box> { type Initialized = Box; + #[inline] fn write_init(mut self, init: impl Init) -> Result { let slot = self.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, @@ -145,6 +148,7 @@ impl InPlaceWrite for Box> { Ok(unsafe { self.assume_init() }) } + #[inline] fn write_pin_init(mut self, init: impl PinInit) -> Result, E> { let slot = self.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 6e9eb90db52c..7600cdbbbf98 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -955,6 +955,7 @@ pub unsafe trait PinInit: Sized { /// Ok(()) /// }); /// ``` + #[inline] fn pin_chain(self, f: F) -> ChainPinInit where F: FnOnce(Pin<&mut T>) -> Result<(), E>, @@ -1003,6 +1004,7 @@ where I: PinInit, F: FnOnce(Pin<&mut T>) -> Result<(), E>, { + #[inline] unsafe fn __init(self, slot: *mut T) -> Result<(), E> { // SAFETY: All requirements fulfilled since this function is `__init`. let slot = unsafe { __internal::Slot::<__internal::Pinned, _>::new(slot) }; @@ -1068,6 +1070,7 @@ pub unsafe trait Init: PinInit { /// Ok(()) /// }); /// ``` + #[inline] fn chain(self, f: F) -> ChainInit where F: FnOnce(&mut T) -> Result<(), E>, @@ -1095,6 +1098,7 @@ where I: Init, F: FnOnce(&mut T) -> Result<(), E>, { + #[inline] unsafe fn __init(self, slot: *mut T) -> Result<(), E> { // SAFETY: All requirements fulfilled since this function is `__init`. let slot = unsafe { __internal::Slot::<__internal::Unpinned, _>::new(slot) }; @@ -1175,6 +1179,7 @@ pub const unsafe fn init_from_closure( /// /// - `*mut U` must be castable to `*mut T` and any value of type `T` written through such a /// pointer must result in a valid `U`. +#[inline] pub const unsafe fn cast_pin_init(init: impl PinInit) -> impl PinInit { // SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety // requirements. @@ -1187,6 +1192,7 @@ pub const unsafe fn cast_pin_init(init: impl PinInit) -> impl Pin /// /// - `*mut U` must be castable to `*mut T` and any value of type `T` written through such a /// pointer must result in a valid `U`. +#[inline] pub const unsafe fn cast_init(init: impl Init) -> impl Init { // SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety // requirements. @@ -1283,6 +1289,7 @@ where /// let array: Box<[usize; 1_000]> = Box::init(init_array_from_fn(|i| i)).unwrap(); /// assert_eq!(array.len(), 1_000); /// ``` +#[inline] pub fn init_array_from_fn( make_init: impl FnMut(usize) -> I, ) -> impl Init<[T; N], E> @@ -1307,6 +1314,7 @@ where /// Arc::pin_init(pin_init_array_from_fn(|i| CMutex::new(i))).unwrap(); /// assert_eq!(array.len(), 1_000); /// ``` +#[inline] pub fn pin_init_array_from_fn( make_init: impl FnMut(usize) -> I, ) -> impl PinInit<[T; N], E> @@ -1342,6 +1350,7 @@ where /// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the /// initializer itself will fail with that error. If it returned `Ok`, then it will run the /// initializer returned by the [`pin_init!`] invocation. +#[inline] pub fn pin_init_scope(make_init: F) -> impl PinInit where F: FnOnce() -> Result, @@ -1385,6 +1394,7 @@ where /// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the /// initializer itself will fail with that error. If it returned `Ok`, then it will run the /// initializer returned by the [`init!`] invocation. +#[inline] pub fn init_scope(make_init: F) -> impl Init where F: FnOnce() -> Result, @@ -1409,6 +1419,7 @@ unsafe impl Init for T {} // SAFETY: the `__init` function always returns `Ok(())` and initializes every field of // `slot`. Additionally, all pinning invariants of `T` are upheld. unsafe impl PinInit for T { + #[inline] unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> { // SAFETY: `slot` is valid for writes by the safety requirements of this function. unsafe { slot.write(self) }; @@ -1423,6 +1434,7 @@ unsafe impl Init for Result {} // - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld. // - `Err(err)`, slot was not written to. unsafe impl PinInit for Result { + #[inline] unsafe fn __init(self, slot: *mut T) -> Result<(), E> { // SAFETY: `slot` is valid for writes by the safety requirements of this function. unsafe { slot.write(self?) }; @@ -1449,6 +1461,7 @@ pub trait InPlaceWrite { impl InPlaceWrite for &'static mut MaybeUninit { type Initialized = &'static mut T; + #[inline] fn write_init(self, init: impl Init) -> Result { let slot = self.as_mut_ptr(); @@ -1459,6 +1472,7 @@ impl InPlaceWrite for &'static mut MaybeUninit { unsafe { Ok(self.assume_init_mut()) } } + #[inline] fn write_pin_init(self, init: impl PinInit) -> Result, E> { let slot = self.as_mut_ptr(); @@ -1764,6 +1778,7 @@ pub trait Wrapper { } impl Wrapper for UnsafeCell { + #[inline] fn pin_init(value_init: impl PinInit) -> impl PinInit { // SAFETY: `UnsafeCell` has a compatible layout to `T`. unsafe { cast_pin_init(value_init) } @@ -1771,6 +1786,7 @@ impl Wrapper for UnsafeCell { } impl Wrapper for MaybeUninit { + #[inline] fn pin_init(value_init: impl PinInit) -> impl PinInit { // SAFETY: `MaybeUninit` has a compatible layout to `T`. unsafe { cast_pin_init(value_init) } @@ -1779,6 +1795,7 @@ impl Wrapper for MaybeUninit { #[cfg(all(feature = "unsafe-pinned", CONFIG_RUSTC_HAS_UNSAFE_PINNED))] impl Wrapper for core::pin::UnsafePinned { + #[inline] fn pin_init(init: impl PinInit) -> impl PinInit { // SAFETY: `UnsafePinned` has a compatible layout to `T`. unsafe { cast_pin_init(init) } -- cgit v1.2.3