From 01f2ffb919adb3c21c76fce1dcfa400aa900f69f Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 22 Apr 2026 17:46:43 +0100 Subject: [PATCH 1/5] internal: init: handle code blocks early `InitializerKind::Code` is a special case where it does not initialize a field, and thus generate no guard and accessors. Handle it earlier and make the rest of the code more linear. Signed-off-by: Gary Guo --- internal/src/init.rs | 100 ++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/internal/src/init.rs b/internal/src/init.rs index b0bfe446..7eda1bcf 100644 --- a/internal/src/init.rs +++ b/internal/src/init.rs @@ -231,6 +231,20 @@ fn init_fields( cfgs.retain(|attr| attr.path().is_ident("cfg")); cfgs }; + + let ident = match kind { + InitializerKind::Value { ident, .. } => ident, + InitializerKind::Init { ident, .. } => ident, + InitializerKind::Code { block, .. } => { + res.extend(quote! { + #(#attrs)* + #[allow(unused_braces)] + #block + }); + continue; + } + }; + let init = match kind { InitializerKind::Value { ident, value } => { let mut value_ident = ident.clone(); @@ -283,55 +297,51 @@ fn init_fields( } } } - InitializerKind::Code { block: value, .. } => quote! { - #(#attrs)* - #[allow(unused_braces)] - #value - }, + InitializerKind::Code { .. } => unreachable!(), }; - res.extend(init); - if let Some(ident) = kind.ident() { - // `mixed_site` ensures that the guard is not accessible to the user-controlled code. - let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); - // NOTE: The reference is derived from the guard so that it only lives as long as the - // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident` - // like the unaligned field guard, it will become effectively `'static`. - let accessor = if pinned { - let project_ident = format_ident!("__project_{ident}"); - quote! { - // SAFETY: the initialization is pinned. - unsafe { #data.#project_ident(#guard.let_binding()) } - } - } else { - quote! { - #guard.let_binding() - } - }; + // `mixed_site` ensures that the guard is not accessible to the user-controlled code. + let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); - res.extend(quote! { - #(#cfgs)* - // Create the drop guard. - // - // SAFETY: - // - `&raw mut (*slot).#ident` is valid. - // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. - // - `(*slot).#ident` has been initialized above. - // - We only need the ownership to the pointee back when initialization has - // succeeded, where we `forget` the guard. - let mut #guard = unsafe { - ::pin_init::__internal::DropGuard::new( - &raw mut (*slot).#ident - ) - }; + // NOTE: The reference is derived from the guard so that it only lives as long as the + // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident` + // like the unaligned field guard, it will become effectively `'static`. + let accessor = if pinned { + let project_ident = format_ident!("__project_{ident}"); + quote! { + // SAFETY: the initialization is pinned. + unsafe { #data.#project_ident(#guard.let_binding()) } + } + } else { + quote! { + #guard.let_binding() + } + }; - #(#cfgs)* - #[allow(unused_variables)] - let #ident = #accessor; - }); - guards.push(guard); - guard_attrs.push(cfgs); - } + res.extend(quote! { + #init + + #(#cfgs)* + // Create the drop guard. + // + // SAFETY: + // - `&raw mut (*slot).#ident` is valid. + // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. + // - `(*slot).#ident` has been initialized above. + // - We only need the ownership to the pointee back when initialization has + // succeeded, where we `forget` the guard. + let mut #guard = unsafe { + ::pin_init::__internal::DropGuard::new( + &raw mut (*slot).#ident + ) + }; + + #(#cfgs)* + #[allow(unused_variables)] + let #ident = #accessor; + }); + guards.push(guard); + guard_attrs.push(cfgs); } quote! { #res From 54654f6959113a8874734714144ae4a63d5cf7ed Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 25 Apr 2026 17:10:07 +0100 Subject: [PATCH 2/5] internal: use marker on drop guard type for pinned fields Instead of projecting the created reference, simply create drop guards with different marker types and have the `let_binding()` method of guards of different marker produce different type instead. This allows more flexible lifetime as this is now controlled by the guard. This will be needed when implementing self-referential fields. Signed-off-by: Gary Guo --- internal/src/init.rs | 47 +++++++++++---------- internal/src/pin_data.rs | 35 +++++++--------- src/__internal.rs | 30 +++++++++++-- tests/ui/expand/many_generics.expanded.rs | 51 ++++++++++++++++------- tests/ui/expand/pin-data.expanded.rs | 34 ++++++++++----- tests/ui/expand/pinned_drop.expanded.rs | 34 ++++++++++----- 6 files changed, 152 insertions(+), 79 deletions(-) diff --git a/internal/src/init.rs b/internal/src/init.rs index 7eda1bcf..a0b3c379 100644 --- a/internal/src/init.rs +++ b/internal/src/init.rs @@ -303,18 +303,31 @@ fn init_fields( // `mixed_site` ensures that the guard is not accessible to the user-controlled code. let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); - // NOTE: The reference is derived from the guard so that it only lives as long as the - // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident` - // like the unaligned field guard, it will become effectively `'static`. - let accessor = if pinned { + let guard_creation = if pinned { let project_ident = format_ident!("__project_{ident}"); quote! { - // SAFETY: the initialization is pinned. - unsafe { #data.#project_ident(#guard.let_binding()) } + // SAFETY: + // - `&raw mut (*slot).#ident` points to the `#ident` field of `slot`. + // - `&raw mut (*slot).#ident` is valid. + // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. + // - `(*slot).#ident` has been initialized above. + // - We only need the ownership to the pointee back when initialization has + // succeeded, where we `forget` the guard. + unsafe { #data.#project_ident(&raw mut (*slot).#ident) } } } else { quote! { - #guard.let_binding() + // SAFETY: + // - `&raw mut (*slot).#ident` is valid. + // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. + // - `(*slot).#ident` has been initialized above. + // - We only need the ownership to the pointee back when initialization has + // succeeded, where we `forget` the guard. + unsafe { + ::pin_init::__internal::DropGuard::<::pin_init::__internal::Unpinned, _>::new( + &raw mut (*slot).#ident + ) + } } }; @@ -322,24 +335,16 @@ fn init_fields( #init #(#cfgs)* - // Create the drop guard. - // - // SAFETY: - // - `&raw mut (*slot).#ident` is valid. - // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. - // - `(*slot).#ident` has been initialized above. - // - We only need the ownership to the pointee back when initialization has - // succeeded, where we `forget` the guard. - let mut #guard = unsafe { - ::pin_init::__internal::DropGuard::new( - &raw mut (*slot).#ident - ) - }; + let mut #guard = #guard_creation; #(#cfgs)* + // NOTE: The reference is derived from the guard so that it only lives as long as the + // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident` + // like the unaligned field guard, it will become effectively `'static`. #[allow(unused_variables)] - let #ident = #accessor; + let #ident = #guard.let_binding(); }); + guards.push(guard); guard_attrs.push(cfgs); } diff --git a/internal/src/pin_data.rs b/internal/src/pin_data.rs index 44d0bd18..90f6b05b 100644 --- a/internal/src/pin_data.rs +++ b/internal/src/pin_data.rs @@ -371,29 +371,18 @@ fn generate_the_pin_data( .as_ref() .expect("only structs with named fields are supported"); let project_ident = format_ident!("__project_{field_name}"); - let (init_ty, init_fn, project_ty, project_body, pin_safety) = if f.pinned { + let (init_ty, init_fn, pin_marker, pin_safety) = if f.pinned { ( quote!(PinInit), quote!(__pinned_init), - quote!(::core::pin::Pin<&'__slot mut #ty>), - // SAFETY: this field is structurally pinned. - quote!(unsafe { ::core::pin::Pin::new_unchecked(slot) }), + quote!(Pinned), quote!( /// - `slot` will not move until it is dropped, i.e. it will be pinned. ), ) } else { - ( - quote!(Init), - quote!(__init), - quote!(&'__slot mut #ty), - quote!(slot), - quote!(), - ) + (quote!(Init), quote!(__init), quote!(Unpinned), quote!()) }; - let slot_safety = format!( - " `slot` points at the field `{field_name}` inside of `{struct_name}`, which is pinned.", - ); quote! { /// # Safety /// @@ -414,13 +403,21 @@ fn generate_the_pin_data( /// # Safety /// - #[doc = #slot_safety] + /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` + /// describes. + /// - `slot` is valid and properly aligned. + /// - `*slot` is initialized, and the ownership is transferred to the returned + /// guard. #(#attrs)* - #vis unsafe fn #project_ident<'__slot>( + #vis unsafe fn #project_ident( self, - slot: &'__slot mut #ty, - ) -> #project_ty { - #project_body + slot: *mut #ty, + ) -> ::pin_init::__internal::DropGuard<::pin_init::__internal::#pin_marker, #ty> { + // SAFETY: + // - If `#pin_marker` is `Pinned`, the corresponding field is structurally + // pinned. + // - Other safety requirements follows the safety requirement. + unsafe { ::pin_init::__internal::DropGuard::new(slot) } } } }) diff --git a/src/__internal.rs b/src/__internal.rs index e54d90a4..010e8bfc 100644 --- a/src/__internal.rs +++ b/src/__internal.rs @@ -277,6 +277,10 @@ fn stack_init_reuse() { println!("{value:?}"); } +// Marker types that determines type of `DropGuard`'s let bindings. +pub struct Pinned; +pub struct Unpinned; + /// When a value of this type is dropped, it drops a `T`. /// /// Can be forgotten to prevent the drop. @@ -285,11 +289,13 @@ fn stack_init_reuse() { /// /// - `ptr` is valid and properly aligned. /// - `*ptr` is initialized and owned by this guard. -pub struct DropGuard { +/// - if `P` is `Pinned`, `ptr` is pinned. +pub struct DropGuard { ptr: *mut T, + phantom: PhantomData

, } -impl DropGuard { +impl DropGuard { /// Creates a drop guard and transfer the ownership of the pointer content. /// /// The ownership is only relinguished if the guard is forgotten via [`core::mem::forget`]. @@ -298,12 +304,18 @@ impl DropGuard { /// /// - `ptr` is valid and properly aligned. /// - `*ptr` is initialized, and the ownership is transferred to this guard. + /// - if `P` is `Pinned`, `ptr` is pinned. #[inline] pub unsafe fn new(ptr: *mut T) -> Self { // INVARIANT: By safety requirement. - Self { ptr } + Self { + ptr, + phantom: PhantomData, + } } +} +impl DropGuard { /// Create a let binding for accessor use. #[inline] pub fn let_binding(&mut self) -> &mut T { @@ -312,7 +324,17 @@ impl DropGuard { } } -impl Drop for DropGuard { +impl DropGuard { + /// Create a let binding for accessor use. + #[inline] + pub fn let_binding(&mut self) -> Pin<&mut T> { + // SAFETY: `self.ptr` is valid, properly aligned, initialized, exclusively accessible and + // pinned per type invariant. + unsafe { Pin::new_unchecked(&mut *self.ptr) } + } +} + +impl Drop for DropGuard { #[inline] fn drop(&mut self) { // SAFETY: `self.ptr` is valid, properly aligned and `*self.ptr` is owned by this guard. diff --git a/tests/ui/expand/many_generics.expanded.rs b/tests/ui/expand/many_generics.expanded.rs index adc83d80..3b3a451e 100644 --- a/tests/ui/expand/many_generics.expanded.rs +++ b/tests/ui/expand/many_generics.expanded.rs @@ -96,12 +96,19 @@ const _: () = { } /// # Safety /// - /// `slot` points at the field `array` inside of `Foo`, which is pinned. - unsafe fn __project_array<'__slot>( + /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` + /// describes. + /// - `slot` is valid and properly aligned. + /// - `*slot` is initialized, and the ownership is transferred to the returned + /// guard. + unsafe fn __project_array( self, - slot: &'__slot mut [u8; 1024 * 1024], - ) -> &'__slot mut [u8; 1024 * 1024] { - slot + slot: *mut [u8; 1024 * 1024], + ) -> ::pin_init::__internal::DropGuard< + ::pin_init::__internal::Unpinned, + [u8; 1024 * 1024], + > { + unsafe { ::pin_init::__internal::DropGuard::new(slot) } } /// # Safety /// @@ -117,12 +124,19 @@ const _: () = { } /// # Safety /// - /// `slot` points at the field `r` inside of `Foo`, which is pinned. - unsafe fn __project_r<'__slot>( + /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` + /// describes. + /// - `slot` is valid and properly aligned. + /// - `*slot` is initialized, and the ownership is transferred to the returned + /// guard. + unsafe fn __project_r( self, - slot: &'__slot mut &'b mut [&'a mut T; SIZE], - ) -> &'__slot mut &'b mut [&'a mut T; SIZE] { - slot + slot: *mut &'b mut [&'a mut T; SIZE], + ) -> ::pin_init::__internal::DropGuard< + ::pin_init::__internal::Unpinned, + &'b mut [&'a mut T; SIZE], + > { + unsafe { ::pin_init::__internal::DropGuard::new(slot) } } /// # Safety /// @@ -139,12 +153,19 @@ const _: () = { } /// # Safety /// - /// `slot` points at the field `_pin` inside of `Foo`, which is pinned. - unsafe fn __project__pin<'__slot>( + /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` + /// describes. + /// - `slot` is valid and properly aligned. + /// - `*slot` is initialized, and the ownership is transferred to the returned + /// guard. + unsafe fn __project__pin( self, - slot: &'__slot mut PhantomPinned, - ) -> ::core::pin::Pin<&'__slot mut PhantomPinned> { - unsafe { ::core::pin::Pin::new_unchecked(slot) } + slot: *mut PhantomPinned, + ) -> ::pin_init::__internal::DropGuard< + ::pin_init::__internal::Pinned, + PhantomPinned, + > { + unsafe { ::pin_init::__internal::DropGuard::new(slot) } } } unsafe impl< diff --git a/tests/ui/expand/pin-data.expanded.rs b/tests/ui/expand/pin-data.expanded.rs index 05c9a119..5b61e89b 100644 --- a/tests/ui/expand/pin-data.expanded.rs +++ b/tests/ui/expand/pin-data.expanded.rs @@ -60,12 +60,19 @@ const _: () = { } /// # Safety /// - /// `slot` points at the field `array` inside of `Foo`, which is pinned. - unsafe fn __project_array<'__slot>( + /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` + /// describes. + /// - `slot` is valid and properly aligned. + /// - `*slot` is initialized, and the ownership is transferred to the returned + /// guard. + unsafe fn __project_array( self, - slot: &'__slot mut [u8; 1024 * 1024], - ) -> &'__slot mut [u8; 1024 * 1024] { - slot + slot: *mut [u8; 1024 * 1024], + ) -> ::pin_init::__internal::DropGuard< + ::pin_init::__internal::Unpinned, + [u8; 1024 * 1024], + > { + unsafe { ::pin_init::__internal::DropGuard::new(slot) } } /// # Safety /// @@ -82,12 +89,19 @@ const _: () = { } /// # Safety /// - /// `slot` points at the field `_pin` inside of `Foo`, which is pinned. - unsafe fn __project__pin<'__slot>( + /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` + /// describes. + /// - `slot` is valid and properly aligned. + /// - `*slot` is initialized, and the ownership is transferred to the returned + /// guard. + unsafe fn __project__pin( self, - slot: &'__slot mut PhantomPinned, - ) -> ::core::pin::Pin<&'__slot mut PhantomPinned> { - unsafe { ::core::pin::Pin::new_unchecked(slot) } + slot: *mut PhantomPinned, + ) -> ::pin_init::__internal::DropGuard< + ::pin_init::__internal::Pinned, + PhantomPinned, + > { + unsafe { ::pin_init::__internal::DropGuard::new(slot) } } } unsafe impl ::pin_init::__internal::HasPinData for Foo { diff --git a/tests/ui/expand/pinned_drop.expanded.rs b/tests/ui/expand/pinned_drop.expanded.rs index 14bbd84e..3317f430 100644 --- a/tests/ui/expand/pinned_drop.expanded.rs +++ b/tests/ui/expand/pinned_drop.expanded.rs @@ -60,12 +60,19 @@ const _: () = { } /// # Safety /// - /// `slot` points at the field `array` inside of `Foo`, which is pinned. - unsafe fn __project_array<'__slot>( + /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` + /// describes. + /// - `slot` is valid and properly aligned. + /// - `*slot` is initialized, and the ownership is transferred to the returned + /// guard. + unsafe fn __project_array( self, - slot: &'__slot mut [u8; 1024 * 1024], - ) -> &'__slot mut [u8; 1024 * 1024] { - slot + slot: *mut [u8; 1024 * 1024], + ) -> ::pin_init::__internal::DropGuard< + ::pin_init::__internal::Unpinned, + [u8; 1024 * 1024], + > { + unsafe { ::pin_init::__internal::DropGuard::new(slot) } } /// # Safety /// @@ -82,12 +89,19 @@ const _: () = { } /// # Safety /// - /// `slot` points at the field `_pin` inside of `Foo`, which is pinned. - unsafe fn __project__pin<'__slot>( + /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` + /// describes. + /// - `slot` is valid and properly aligned. + /// - `*slot` is initialized, and the ownership is transferred to the returned + /// guard. + unsafe fn __project__pin( self, - slot: &'__slot mut PhantomPinned, - ) -> ::core::pin::Pin<&'__slot mut PhantomPinned> { - unsafe { ::core::pin::Pin::new_unchecked(slot) } + slot: *mut PhantomPinned, + ) -> ::pin_init::__internal::DropGuard< + ::pin_init::__internal::Pinned, + PhantomPinned, + > { + unsafe { ::pin_init::__internal::DropGuard::new(slot) } } } unsafe impl ::pin_init::__internal::HasPinData for Foo { From e060119d640139d4d0498edb5926d168f5095293 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 11 May 2026 16:20:07 +0100 Subject: [PATCH 3/5] internal: make `make_closure` inherent methods The `InitData` and `PinData` traits do not need to exist, the inference helpers could be inherent methods instead. There is no risk for calling the wrong methods even when user defines it, as inherent methods take priority over trait methods. With this change, it unlocks the possibility of attaching additional bounds to the method per type, which is not possible for trait methods. Signed-off-by: Gary Guo --- internal/src/init.rs | 7 +-- internal/src/pin_data.rs | 17 +++++--- src/__internal.rs | 52 +++++------------------ tests/ui/expand/many_generics.expanded.rs | 21 +++++---- tests/ui/expand/pin-data.expanded.rs | 13 ++++-- tests/ui/expand/pinned_drop.expanded.rs | 13 ++++-- tests/ui/expand/simple-init.expanded.rs | 14 +++--- 7 files changed, 59 insertions(+), 78 deletions(-) diff --git a/internal/src/init.rs b/internal/src/init.rs index a0b3c379..11affa76 100644 --- a/internal/src/init.rs +++ b/internal/src/init.rs @@ -103,17 +103,15 @@ pub(crate) fn expand( |(_, err)| Box::new(err), ); let slot = format_ident!("slot"); - let (has_data_trait, data_trait, get_data, init_from_closure) = if pinned { + let (has_data_trait, get_data, init_from_closure) = if pinned { ( format_ident!("HasPinData"), - format_ident!("PinData"), format_ident!("__pin_data"), format_ident!("pin_init_from_closure"), ) } else { ( format_ident!("HasInitData"), - format_ident!("InitData"), format_ident!("__init_data"), format_ident!("init_from_closure"), ) @@ -157,8 +155,7 @@ pub(crate) fn expand( #path::#get_data() }; // Ensure that `#data` really is of type `#data` and help with type inference: - let init = ::pin_init::__internal::#data_trait::make_closure::<_, #error>( - #data, + let init = #data.__make_closure::<_, #error>( move |slot| { #zeroable_check #this diff --git a/internal/src/pin_data.rs b/internal/src/pin_data.rs index 90f6b05b..713a43c2 100644 --- a/internal/src/pin_data.rs +++ b/internal/src/pin_data.rs @@ -447,6 +447,16 @@ fn generate_the_pin_data( impl #impl_generics __ThePinData #ty_generics #whr { + /// Type inference helper function. + #[inline(always)] + #vis fn __make_closure<__F, __E>(self, f: __F) -> __F + where + __F: FnOnce(*mut #struct_name #ty_generics) -> + ::core::result::Result<::pin_init::__internal::InitOk, __E>, + { + f + } + #field_accessors } @@ -461,13 +471,6 @@ fn generate_the_pin_data( __ThePinData { __phantom: ::pin_init::__internal::PhantomInvariant::new() } } } - - // SAFETY: TODO - unsafe impl #impl_generics ::pin_init::__internal::PinData for __ThePinData #ty_generics - #whr - { - type Datee = #struct_name #ty_generics; - } } } diff --git a/src/__internal.rs b/src/__internal.rs index 010e8bfc..d7fdcfef 100644 --- a/src/__internal.rs +++ b/src/__internal.rs @@ -113,30 +113,12 @@ impl InitOk { /// /// Only the `init` module is allowed to use this trait. pub unsafe trait HasPinData { - type PinData: PinData; + type PinData; #[expect(clippy::missing_safety_doc)] 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. - #[inline(always)] - fn make_closure(self, f: F) -> F - where - F: FnOnce(*mut Self::Datee) -> Result, - { - f - } -} - /// This trait is automatically implemented for every type. It aims to provide the same type /// inference help as `HasPinData`. /// @@ -144,30 +126,12 @@ pub unsafe trait PinData: Copy { /// /// Only the `init` module is allowed to use this trait. pub unsafe trait HasInitData { - type InitData: InitData; + type InitData; #[expect(clippy::missing_safety_doc)] 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. - #[inline(always)] - fn make_closure(self, f: F) -> F - where - F: FnOnce(*mut Self::Datee) -> Result, - { - f - } -} - pub struct AllData(PhantomInvariant); impl Clone for AllData { @@ -178,9 +142,15 @@ impl Clone for AllData { impl Copy for AllData {} -// SAFETY: TODO. -unsafe impl InitData for AllData { - type Datee = T; +impl AllData { + /// Type inference helper function. + #[inline(always)] + pub fn __make_closure(self, f: F) -> F + where + F: FnOnce(*mut T) -> Result, + { + f + } } // SAFETY: TODO. diff --git a/tests/ui/expand/many_generics.expanded.rs b/tests/ui/expand/many_generics.expanded.rs index 3b3a451e..cf1a9e51 100644 --- a/tests/ui/expand/many_generics.expanded.rs +++ b/tests/ui/expand/many_generics.expanded.rs @@ -82,6 +82,16 @@ const _: () = { where T: Bar<'a, 1>, { + /// Type inference helper function. + #[inline(always)] + fn __make_closure<__F, __E>(self, f: __F) -> __F + where + __F: FnOnce( + *mut Foo<'a, 'b, T, SIZE>, + ) -> ::core::result::Result<::pin_init::__internal::InitOk, __E>, + { + f + } /// # Safety /// /// - `slot` is a valid pointer to uninitialized memory. @@ -184,17 +194,6 @@ const _: () = { } } } - unsafe impl< - 'a, - 'b: 'a, - T: Bar<'b> + ?Sized + 'a, - const SIZE: usize, - > ::pin_init::__internal::PinData for __ThePinData<'a, 'b, T, SIZE> - where - T: Bar<'a, 1>, - { - type Datee = Foo<'a, 'b, T, SIZE>; - } #[allow(dead_code)] struct __Unpin<'__pin, 'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize = 0> where diff --git a/tests/ui/expand/pin-data.expanded.rs b/tests/ui/expand/pin-data.expanded.rs index 5b61e89b..cbc0f2e8 100644 --- a/tests/ui/expand/pin-data.expanded.rs +++ b/tests/ui/expand/pin-data.expanded.rs @@ -46,6 +46,16 @@ const _: () = { #[allow(dead_code)] #[expect(clippy::missing_safety_doc)] impl __ThePinData { + /// Type inference helper function. + #[inline(always)] + fn __make_closure<__F, __E>(self, f: __F) -> __F + where + __F: FnOnce( + *mut Foo, + ) -> ::core::result::Result<::pin_init::__internal::InitOk, __E>, + { + f + } /// # Safety /// /// - `slot` is a valid pointer to uninitialized memory. @@ -112,9 +122,6 @@ const _: () = { } } } - unsafe impl ::pin_init::__internal::PinData for __ThePinData { - type Datee = Foo; - } #[allow(dead_code)] struct __Unpin<'__pin> { __phantom_pin: ::pin_init::__internal::PhantomInvariantLifetime<'__pin>, diff --git a/tests/ui/expand/pinned_drop.expanded.rs b/tests/ui/expand/pinned_drop.expanded.rs index 3317f430..e650a26d 100644 --- a/tests/ui/expand/pinned_drop.expanded.rs +++ b/tests/ui/expand/pinned_drop.expanded.rs @@ -46,6 +46,16 @@ const _: () = { #[allow(dead_code)] #[expect(clippy::missing_safety_doc)] impl __ThePinData { + /// Type inference helper function. + #[inline(always)] + fn __make_closure<__F, __E>(self, f: __F) -> __F + where + __F: FnOnce( + *mut Foo, + ) -> ::core::result::Result<::pin_init::__internal::InitOk, __E>, + { + f + } /// # Safety /// /// - `slot` is a valid pointer to uninitialized memory. @@ -112,9 +122,6 @@ const _: () = { } } } - unsafe impl ::pin_init::__internal::PinData for __ThePinData { - type Datee = Foo; - } #[allow(dead_code)] struct __Unpin<'__pin> { __phantom_pin: ::pin_init::__internal::PhantomInvariantLifetime<'__pin>, diff --git a/tests/ui/expand/simple-init.expanded.rs b/tests/ui/expand/simple-init.expanded.rs index f3411bc9..ee3d0096 100644 --- a/tests/ui/expand/simple-init.expanded.rs +++ b/tests/ui/expand/simple-init.expanded.rs @@ -6,17 +6,15 @@ fn main() { use ::pin_init::__internal::HasInitData; Foo::__init_data() }; - let init = ::pin_init::__internal::InitData::make_closure::< - _, - ::core::convert::Infallible, - >( - __data, - move |slot| { + let init = __data + .__make_closure::< + _, + ::core::convert::Infallible, + >(move |slot| { #[allow(unreachable_code, clippy::diverging_sub_expression)] let _ = || unsafe { ::core::ptr::write(slot, Foo {}) }; Ok(unsafe { ::pin_init::__internal::InitOk::new() }) - }, - ); + }); let init = move | slot, | -> ::core::result::Result<(), ::core::convert::Infallible> { From 318685102b829f227dd4b746a50dc2b7183a8fc9 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 22 Apr 2026 20:13:43 +0100 Subject: [PATCH 4/5] internal: project slots instead of references By projecting slots, the `pin_init!` and `init!` code path can be more unified. This also reduces the amount of macro-generated code and shifts them to the shared infrastructure. Signed-off-by: Gary Guo --- internal/src/init.rs | 113 ++++++------------ internal/src/pin_data.rs | 54 +++------ src/__internal.rs | 77 ++++++++++++ src/lib.rs | 12 +- .../init/colon_instead_of_arrow.stderr | 23 ++-- .../init/field_value_wrong_type.stderr | 23 ++-- .../ui/compile-fail/init/invalid_init.stderr | 9 +- .../init/missing_comma_with_zeroable.stderr | 28 +++-- .../compile-fail/init/shadowing_field.stderr | 23 ++-- .../compile-fail/pin_data/missing_pin.stderr | 21 ++-- tests/ui/expand/many_generics.expanded.rs | 85 ++++--------- tests/ui/expand/pin-data.expanded.rs | 57 +++------ tests/ui/expand/pinned_drop.expanded.rs | 57 +++------ 13 files changed, 273 insertions(+), 309 deletions(-) diff --git a/internal/src/init.rs b/internal/src/init.rs index 11affa76..e6f5ea06 100644 --- a/internal/src/init.rs +++ b/internal/src/init.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use proc_macro2::{Span, TokenStream}; -use quote::{format_ident, quote, quote_spanned}; +use quote::{format_ident, quote}; use syn::{ braced, parse::{End, Parse}, @@ -242,102 +242,61 @@ fn init_fields( } }; - let init = match kind { - InitializerKind::Value { ident, value } => { - let mut value_ident = ident.clone(); - let value_prep = value.as_ref().map(|value| &value.1).map(|value| { - // Setting the span of `value_ident` to `value`'s span improves error messages - // when the type of `value` is wrong. - value_ident.set_span(value.span()); - quote!(let #value_ident = #value;) - }); - // Again span for better diagnostics - let write = quote_spanned!(ident.span()=> ::core::ptr::write); - quote! { - #(#attrs)* - { - #value_prep - // SAFETY: TODO - unsafe { #write(&raw mut (*#slot).#ident, #value_ident) }; - } - } - } - InitializerKind::Init { ident, value, .. } => { - // Again span for better diagnostics - let init = format_ident!("init", span = value.span()); - let value_init = if pinned { - quote! { - // SAFETY: - // - `slot` is valid, because we are inside of an initializer closure, we - // return when an error/panic occurs. - // - We also use `#data` to require the correct trait (`Init` or `PinInit`) - // for `#ident`. - unsafe { #data.#ident(&raw mut (*#slot).#ident, #init)? }; - } - } else { - quote! { - // SAFETY: `slot` is valid, because we are inside of an initializer - // closure, we return when an error/panic occurs. - unsafe { - ::pin_init::Init::__init( - #init, - &raw mut (*#slot).#ident, - )? - }; - } - }; - quote! { - #(#attrs)* - { - let #init = #value; - #value_init - } - } - } - InitializerKind::Code { .. } => unreachable!(), - }; - - // `mixed_site` ensures that the guard is not accessible to the user-controlled code. - let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); - - let guard_creation = if pinned { - let project_ident = format_ident!("__project_{ident}"); + let slot = if pinned { quote! { // SAFETY: // - `&raw mut (*slot).#ident` points to the `#ident` field of `slot`. // - `&raw mut (*slot).#ident` is valid. // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. - // - `(*slot).#ident` has been initialized above. - // - We only need the ownership to the pointee back when initialization has - // succeeded, where we `forget` the guard. - unsafe { #data.#project_ident(&raw mut (*slot).#ident) } + // - `make_field_check` prevents `#ident` from being used twice, therefore + // `(*slot).#ident` is exclusively accessed and has not been initialized. + (unsafe { #data.#ident(&raw mut (*#slot).#ident) }) } } else { quote! { + // For `init!()` macro, everything is unpinned. // SAFETY: // - `&raw mut (*slot).#ident` is valid. // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. - // - `(*slot).#ident` has been initialized above. - // - We only need the ownership to the pointee back when initialization has - // succeeded, where we `forget` the guard. - unsafe { - ::pin_init::__internal::DropGuard::<::pin_init::__internal::Unpinned, _>::new( - &raw mut (*slot).#ident + // - `make_field_check` prevents `#ident` from being used twice, therefore + // `(*slot).#ident` is exclusively accessed and has not been initialized. + (unsafe { + ::pin_init::__internal::Slot::<::pin_init::__internal::Unpinned, _>::new( + &raw mut (*#slot).#ident ) + }) + } + }; + + // `mixed_site` ensures that the guard is not accessible to the user-controlled code. + let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); + + let init = match kind { + InitializerKind::Value { ident, value } => { + let value = value + .as_ref() + .map(|(_, value)| quote!(#value)) + .unwrap_or_else(|| quote!(#ident)); + + quote! { + #(#attrs)* + let mut #guard = #slot.write(#value); + } } + InitializerKind::Init { value, .. } => { + quote! { + #(#attrs)* + let mut #guard = #slot.init(#value)?; + } + } + InitializerKind::Code { .. } => unreachable!(), }; res.extend(quote! { #init #(#cfgs)* - let mut #guard = #guard_creation; - - #(#cfgs)* - // NOTE: The reference is derived from the guard so that it only lives as long as the - // guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident` - // like the unaligned field guard, it will become effectively `'static`. #[allow(unused_variables)] let #ident = #guard.let_binding(); }); diff --git a/internal/src/pin_data.rs b/internal/src/pin_data.rs index 713a43c2..926f84a0 100644 --- a/internal/src/pin_data.rs +++ b/internal/src/pin_data.rs @@ -352,10 +352,9 @@ fn generate_the_pin_data( let (impl_generics, ty_generics, whr) = generics.split_for_impl(); // For every field, we create an initializing projection function according to its projection - // type. If a field is structurally pinned, then it must be initialized via `PinInit`, if it is - // not structurally pinned, then it can be initialized via `Init`. - // - // The functions are `unsafe` to prevent accidentally calling them. + // type. If a field is structurally pinned, we create a `Slot` with `Pinned` which must be + // initialized via `PinInit`; if it is not structurally pinned, then we create a `Slot` with + // `Unpinned` which allows initialization via `Init`. let field_accessors = fields .iter() .map(|f| { @@ -370,54 +369,29 @@ fn generate_the_pin_data( let field_name = ident .as_ref() .expect("only structs with named fields are supported"); - let project_ident = format_ident!("__project_{field_name}"); - let (init_ty, init_fn, pin_marker, pin_safety) = if f.pinned { - ( - quote!(PinInit), - quote!(__pinned_init), - quote!(Pinned), - quote!( - /// - `slot` will not move until it is dropped, i.e. it will be pinned. - ), - ) + let pin_marker = if f.pinned { + quote!(Pinned) } else { - (quote!(Init), quote!(__init), quote!(Unpinned), quote!()) + quote!(Unpinned) }; quote! { /// # 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. - #pin_safety - #(#attrs)* - #vis unsafe fn #field_name( - self, - slot: *mut #ty, - init: impl ::pin_init::#init_ty<#ty, E>, - ) -> ::core::result::Result<(), E> { - // SAFETY: this function has the same safety requirements as the __init function - // called below. - unsafe { ::pin_init::#init_ty::#init_fn(init, slot) } - } - - /// # Safety - /// - /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` - /// describes. - /// - `slot` is valid and properly aligned. - /// - `*slot` is initialized, and the ownership is transferred to the returned - /// guard. + /// - `slot` points to a `#field_name` field of a pinned struct that this + /// `__ThePinData` describes. + /// - `slot` is a valid, properly aligned and points to uninitialized and + /// exclusively accessed memory. #(#attrs)* - #vis unsafe fn #project_ident( + #[inline(always)] + #vis unsafe fn #field_name( self, slot: *mut #ty, - ) -> ::pin_init::__internal::DropGuard<::pin_init::__internal::#pin_marker, #ty> { + ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> { // SAFETY: // - If `#pin_marker` is `Pinned`, the corresponding field is structurally // pinned. // - Other safety requirements follows the safety requirement. - unsafe { ::pin_init::__internal::DropGuard::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(slot) } } } }) diff --git a/src/__internal.rs b/src/__internal.rs index d7fdcfef..540add6c 100644 --- a/src/__internal.rs +++ b/src/__internal.rs @@ -251,6 +251,83 @@ fn stack_init_reuse() { pub struct Pinned; pub struct Unpinned; +/// Represent an uninitialized field. +/// +/// # Invariants +/// +/// - `ptr` is valid, properly aligned and points to uninitialized and exclusively accessed memory. +/// - If `P` is `Pinned`, then `ptr` is structurally pinned. +pub struct Slot { + ptr: *mut T, + _phantom: PhantomData

, +} + +impl Slot { + /// # Safety + /// + /// - `ptr` is valid, properly aligned and points to uninitialized and exclusively accessed + /// memory. + /// - If `P` is `Pinned`, then `ptr` is structurally pinned. + #[inline(always)] + pub unsafe fn new(ptr: *mut T) -> Self { + // INVARIANT: Per safety requirement. + Self { + ptr, + _phantom: PhantomData, + } + } + + /// Initialize the field by value. + #[inline(always)] + pub fn write(self, value: T) -> DropGuard + where + T: Sized, + { + // SAFETY: `self.ptr` is a valid and aligned pointer for write. + unsafe { self.ptr.write(value) } + // SAFETY: + // - `self.ptr` is valid and properly aligned per type invariant. + // - `*self.ptr` is initialized above and the ownership is transferred to the guard. + // - If `P` is `Pinned`, `self.ptr` is pinned. + unsafe { DropGuard::new(self.ptr) } + } +} + +impl Slot { + /// Initialize the field. + #[inline(always)] + pub fn init(self, init: impl Init) -> Result, E> { + // SAFETY: + // - `self.ptr` is valid and properly aligned. + // - when `Err` is returned, we also propagate the error without touching `slot`; + // also `self` is consumed so it cannot be touched further. + unsafe { init.__init(self.ptr)? }; + + // SAFETY: + // - `self.ptr` is valid and properly aligned per type invariant. + // - `*self.ptr` is initialized above and the ownership is transferred to the guard. + Ok(unsafe { DropGuard::new(self.ptr) }) + } +} + +impl Slot { + /// Initialize the field. + #[inline(always)] + pub fn init(self, init: impl PinInit) -> Result, E> { + // SAFETY: + // - `self.ptr` is valid and properly aligned. + // - 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)? }; + + // SAFETY: + // - `self.ptr` is valid, properly aligned and pinned per type invariant. + // - `*self.ptr` is initialized above and the ownership is transferred to the guard. + Ok(unsafe { DropGuard::new(self.ptr) }) + } +} + /// When a value of this type is dropped, it drops a `T`. /// /// Can be forgotten to prevent the drop. diff --git a/src/lib.rs b/src/lib.rs index 4098c65d..e891d65c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -867,12 +867,12 @@ pub use pin_init_internal::init; #[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::__internal::HasPinData>::__pin_data() }; - let init = $crate::__internal::AlwaysFail::<$field_ty>::new(); - // SAFETY: This code is unreachable. - unsafe { data.$field(ptr, init) }.ok(); + // SAFETY: This code is unreachable. + let _ = move |ptr: *mut $field_ty| unsafe { + let data = <$ty as $crate::__internal::HasPinData>::__pin_data(); + _ = data + .$field(ptr) + .init($crate::__internal::AlwaysFail::<$field_ty>::new()); }; }; diff --git a/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr b/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr index f5d9ee7b..bc6ee146 100644 --- a/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr +++ b/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr @@ -5,14 +5,23 @@ error[E0308]: mismatched types | ------------------ the found opaque type ... 21 | pin_init!(Self { bar: Bar::new() }) - | --- ^^^^^^^^^^ expected `Bar`, found opaque type - | | - | arguments to this function are incorrect + | ----------------------^^^^^^^^^^--- + | | | + | | expected `Bar`, found opaque type + | arguments to this method are incorrect | = note: expected struct `Bar` found opaque type `impl pin_init::PinInit` -note: function defined here - --> $RUST/core/src/ptr/mod.rs +help: the return type of this call is `impl pin_init::PinInit` due to the type of the argument passed + --> tests/ui/compile-fail/init/colon_instead_of_arrow.rs:21:9 | - | pub const unsafe fn write(dst: *mut T, src: T) { - | ^^^^^ +21 | pin_init!(Self { bar: Bar::new() }) + | ^^^^^^^^^^^^^^^^^^^^^^----------^^^ + | | + | this argument influences the return type of `write` +note: method defined here + --> src/__internal.rs + | + | pub fn write(self, value: T) -> DropGuard + | ^^^^^ + = note: this error originates in the macro `pin_init` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/compile-fail/init/field_value_wrong_type.stderr b/tests/ui/compile-fail/init/field_value_wrong_type.stderr index f6470767..d506bf0b 100644 --- a/tests/ui/compile-fail/init/field_value_wrong_type.stderr +++ b/tests/ui/compile-fail/init/field_value_wrong_type.stderr @@ -2,12 +2,21 @@ error[E0308]: mismatched types --> tests/ui/compile-fail/init/field_value_wrong_type.rs:8:28 | 8 | let _ = init!(Foo { a: () }); - | - ^^ expected `usize`, found `()` - | | - | arguments to this function are incorrect + | ---------------^^--- + | | | + | | expected `usize`, found `()` + | arguments to this method are incorrect | -note: function defined here - --> $RUST/core/src/ptr/mod.rs +help: the return type of this call is `()` due to the type of the argument passed + --> tests/ui/compile-fail/init/field_value_wrong_type.rs:8:13 | - | pub const unsafe fn write(dst: *mut T, src: T) { - | ^^^^^ +8 | let _ = init!(Foo { a: () }); + | ^^^^^^^^^^^^^^^--^^^ + | | + | this argument influences the return type of `write` +note: method defined here + --> src/__internal.rs + | + | pub fn write(self, value: T) -> DropGuard + | ^^^^^ + = note: this error originates in the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/compile-fail/init/invalid_init.stderr b/tests/ui/compile-fail/init/invalid_init.stderr index dd317b60..137c1fae 100644 --- a/tests/ui/compile-fail/init/invalid_init.stderr +++ b/tests/ui/compile-fail/init/invalid_init.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `impl pin_init::PinInit: Init` is not satisfied +error[E0277]: the trait bound `impl pin_init::PinInit: Init` is not satisfied --> tests/ui/compile-fail/init/invalid_init.rs:19:16 | 18 | let _ = init!(Foo { | _____________- 19 | | bar <- Bar::new(), - | | ^^^^^^^^^^ the trait `Init` is not implemented for `impl pin_init::PinInit` + | | ^^^^^^^^^^ the trait `Init` is not implemented for `impl pin_init::PinInit` 20 | | }); | |______- required by a bound introduced by this call | @@ -19,3 +19,8 @@ help: the following other types implement trait `Init` ... | unsafe impl Init for Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Result` +note: required by a bound in `pin_init::__internal::Slot::::init` + --> src/__internal.rs + | + | pub fn init(self, init: impl Init) -> Result, E> { + | ^^^^^^^^^^ required by this bound in `Slot::::init` diff --git a/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr b/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr index 865615d2..a2978266 100644 --- a/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr +++ b/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr @@ -1,18 +1,30 @@ error[E0308]: mismatched types --> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:12:12 | -12 | a: 0..Zeroable::init_zeroed() - | - ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Range<{integer}>` - | | - | arguments to this function are incorrect +11 | let _ = init!(Foo { + | _____________- +12 | | a: 0..Zeroable::init_zeroed() + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Range<{integer}>` +13 | | }); + | |______- arguments to this method are incorrect | = note: expected type `usize` found struct `std::ops::Range<{integer}>` -note: function defined here - --> $RUST/core/src/ptr/mod.rs +help: the return type of this call is `std::ops::Range<{integer}>` due to the type of the argument passed + --> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:11:13 | - | pub const unsafe fn write(dst: *mut T, src: T) { - | ^^^^^ +11 | let _ = init!(Foo { + | _____________^ +12 | | a: 0..Zeroable::init_zeroed() + | | -------------------------- this argument influences the return type of `write` +13 | | }); + | |______^ +note: method defined here + --> src/__internal.rs + | + | pub fn write(self, value: T) -> DropGuard + | ^^^^^ + = note: this error originates in the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0063]: missing field `b` in initializer of `Foo` --> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:11:19 diff --git a/tests/ui/compile-fail/init/shadowing_field.stderr b/tests/ui/compile-fail/init/shadowing_field.stderr index d9ba3f59..af21f62a 100644 --- a/tests/ui/compile-fail/init/shadowing_field.stderr +++ b/tests/ui/compile-fail/init/shadowing_field.stderr @@ -2,15 +2,24 @@ error[E0308]: mismatched types --> tests/ui/compile-fail/init/shadowing_field.rs:10:31 | 10 | let _ = init!(Foo { x, y: x }); - | - ^ expected `usize`, found `&mut usize` - | | - | arguments to this function are incorrect + | ------------------^--- + | | | + | | expected `usize`, found `&mut usize` + | arguments to this method are incorrect | -note: function defined here - --> $RUST/core/src/ptr/mod.rs +help: the return type of this call is `&mut usize` due to the type of the argument passed + --> tests/ui/compile-fail/init/shadowing_field.rs:10:13 | - | pub const unsafe fn write(dst: *mut T, src: T) { - | ^^^^^ +10 | let _ = init!(Foo { x, y: x }); + | ^^^^^^^^^^^^^^^^^^-^^^ + | | + | this argument influences the return type of `write` +note: method defined here + --> src/__internal.rs + | + | pub fn write(self, value: T) -> DropGuard + | ^^^^^ + = note: this error originates in the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider dereferencing the borrow | 10 | let _ = init!(Foo { x, y: *x }); diff --git a/tests/ui/compile-fail/pin_data/missing_pin.stderr b/tests/ui/compile-fail/pin_data/missing_pin.stderr index 3985b4b1..1ebbbdbf 100644 --- a/tests/ui/compile-fail/pin_data/missing_pin.stderr +++ b/tests/ui/compile-fail/pin_data/missing_pin.stderr @@ -1,10 +1,11 @@ error[E0277]: the trait bound `impl PinInit: Init` is not satisfied --> tests/ui/compile-fail/pin_data/missing_pin.rs:12:18 | -12 | a <- a, - | - ^ the trait `Init` is not implemented for `impl PinInit` - | | - | required by a bound introduced by this call +11 | / pin_init!(Self { +12 | | a <- a, + | | ^ the trait `Init` is not implemented for `impl PinInit` +13 | | }) + | |__________- required by a bound introduced by this call | help: the trait `Init` is not implemented for `impl PinInit` but trait `Init, Infallible>` is implemented for it @@ -13,12 +14,8 @@ help: the trait `Init` is not implemented for `impl PinInit` | unsafe impl Init for T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: for that trait implementation, expected `impl PinInit`, found `usize` -note: required by a bound in `__ThePinData::a` - --> tests/ui/compile-fail/pin_data/missing_pin.rs:4:1 +note: required by a bound in `pin_init::__internal::Slot::::init` + --> src/__internal.rs | - 4 | #[pin_data] - | ^^^^^^^^^^^ required by this bound in `__ThePinData::a` - 5 | struct Foo { - 6 | a: usize, - | - required by a bound in this associated function - = note: this error originates in the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + | pub fn init(self, init: impl Init) -> Result, E> { + | ^^^^^^^^^^ required by this bound in `Slot::::init` diff --git a/tests/ui/expand/many_generics.expanded.rs b/tests/ui/expand/many_generics.expanded.rs index cf1a9e51..45e8873d 100644 --- a/tests/ui/expand/many_generics.expanded.rs +++ b/tests/ui/expand/many_generics.expanded.rs @@ -94,88 +94,51 @@ const _: () = { } /// # 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 array( - self, - slot: *mut [u8; 1024 * 1024], - init: impl ::pin_init::Init<[u8; 1024 * 1024], E>, - ) -> ::core::result::Result<(), E> { - unsafe { ::pin_init::Init::__init(init, slot) } - } - /// # Safety - /// - /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` - /// describes. - /// - `slot` is valid and properly aligned. - /// - `*slot` is initialized, and the ownership is transferred to the returned - /// guard. - unsafe fn __project_array( + /// - `slot` points to a `#field_name` field of a pinned struct that this + /// `__ThePinData` describes. + /// - `slot` is a valid, properly aligned and points to uninitialized and + /// exclusively accessed memory. + #[inline(always)] + unsafe fn array( self, slot: *mut [u8; 1024 * 1024], - ) -> ::pin_init::__internal::DropGuard< + ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Unpinned, [u8; 1024 * 1024], > { - unsafe { ::pin_init::__internal::DropGuard::new(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 r( - self, - slot: *mut &'b mut [&'a mut T; SIZE], - init: impl ::pin_init::Init<&'b mut [&'a mut T; SIZE], E>, - ) -> ::core::result::Result<(), E> { - unsafe { ::pin_init::Init::__init(init, slot) } + unsafe { ::pin_init::__internal::Slot::new(slot) } } /// # Safety /// - /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` - /// describes. - /// - `slot` is valid and properly aligned. - /// - `*slot` is initialized, and the ownership is transferred to the returned - /// guard. - unsafe fn __project_r( + /// - `slot` points to a `#field_name` field of a pinned struct that this + /// `__ThePinData` describes. + /// - `slot` is a valid, properly aligned and points to uninitialized and + /// exclusively accessed memory. + #[inline(always)] + unsafe fn r( self, slot: *mut &'b mut [&'a mut T; SIZE], - ) -> ::pin_init::__internal::DropGuard< + ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Unpinned, &'b mut [&'a mut T; SIZE], > { - unsafe { ::pin_init::__internal::DropGuard::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(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. - /// - `slot` will not move until it is dropped, i.e. it will be pinned. - unsafe fn _pin( - self, - slot: *mut PhantomPinned, - init: impl ::pin_init::PinInit, - ) -> ::core::result::Result<(), E> { - unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } - } - /// # Safety - /// - /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` - /// describes. - /// - `slot` is valid and properly aligned. - /// - `*slot` is initialized, and the ownership is transferred to the returned - /// guard. - unsafe fn __project__pin( + /// - `slot` points to a `#field_name` field of a pinned struct that this + /// `__ThePinData` describes. + /// - `slot` is a valid, properly aligned and points to uninitialized and + /// exclusively accessed memory. + #[inline(always)] + unsafe fn _pin( self, slot: *mut PhantomPinned, - ) -> ::pin_init::__internal::DropGuard< + ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Pinned, PhantomPinned, > { - unsafe { ::pin_init::__internal::DropGuard::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(slot) } } } unsafe impl< diff --git a/tests/ui/expand/pin-data.expanded.rs b/tests/ui/expand/pin-data.expanded.rs index cbc0f2e8..5b490d99 100644 --- a/tests/ui/expand/pin-data.expanded.rs +++ b/tests/ui/expand/pin-data.expanded.rs @@ -58,60 +58,35 @@ const _: () = { } /// # 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 array( - self, - slot: *mut [u8; 1024 * 1024], - init: impl ::pin_init::Init<[u8; 1024 * 1024], E>, - ) -> ::core::result::Result<(), E> { - unsafe { ::pin_init::Init::__init(init, slot) } - } - /// # Safety - /// - /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` - /// describes. - /// - `slot` is valid and properly aligned. - /// - `*slot` is initialized, and the ownership is transferred to the returned - /// guard. - unsafe fn __project_array( + /// - `slot` points to a `#field_name` field of a pinned struct that this + /// `__ThePinData` describes. + /// - `slot` is a valid, properly aligned and points to uninitialized and + /// exclusively accessed memory. + #[inline(always)] + unsafe fn array( self, slot: *mut [u8; 1024 * 1024], - ) -> ::pin_init::__internal::DropGuard< + ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Unpinned, [u8; 1024 * 1024], > { - unsafe { ::pin_init::__internal::DropGuard::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(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. - /// - `slot` will not move until it is dropped, i.e. it will be pinned. - unsafe fn _pin( - self, - slot: *mut PhantomPinned, - init: impl ::pin_init::PinInit, - ) -> ::core::result::Result<(), E> { - unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } - } - /// # Safety - /// - /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` - /// describes. - /// - `slot` is valid and properly aligned. - /// - `*slot` is initialized, and the ownership is transferred to the returned - /// guard. - unsafe fn __project__pin( + /// - `slot` points to a `#field_name` field of a pinned struct that this + /// `__ThePinData` describes. + /// - `slot` is a valid, properly aligned and points to uninitialized and + /// exclusively accessed memory. + #[inline(always)] + unsafe fn _pin( self, slot: *mut PhantomPinned, - ) -> ::pin_init::__internal::DropGuard< + ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Pinned, PhantomPinned, > { - unsafe { ::pin_init::__internal::DropGuard::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(slot) } } } unsafe impl ::pin_init::__internal::HasPinData for Foo { diff --git a/tests/ui/expand/pinned_drop.expanded.rs b/tests/ui/expand/pinned_drop.expanded.rs index e650a26d..58dcf372 100644 --- a/tests/ui/expand/pinned_drop.expanded.rs +++ b/tests/ui/expand/pinned_drop.expanded.rs @@ -58,60 +58,35 @@ const _: () = { } /// # 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 array( - self, - slot: *mut [u8; 1024 * 1024], - init: impl ::pin_init::Init<[u8; 1024 * 1024], E>, - ) -> ::core::result::Result<(), E> { - unsafe { ::pin_init::Init::__init(init, slot) } - } - /// # Safety - /// - /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` - /// describes. - /// - `slot` is valid and properly aligned. - /// - `*slot` is initialized, and the ownership is transferred to the returned - /// guard. - unsafe fn __project_array( + /// - `slot` points to a `#field_name` field of a pinned struct that this + /// `__ThePinData` describes. + /// - `slot` is a valid, properly aligned and points to uninitialized and + /// exclusively accessed memory. + #[inline(always)] + unsafe fn array( self, slot: *mut [u8; 1024 * 1024], - ) -> ::pin_init::__internal::DropGuard< + ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Unpinned, [u8; 1024 * 1024], > { - unsafe { ::pin_init::__internal::DropGuard::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(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. - /// - `slot` will not move until it is dropped, i.e. it will be pinned. - unsafe fn _pin( - self, - slot: *mut PhantomPinned, - init: impl ::pin_init::PinInit, - ) -> ::core::result::Result<(), E> { - unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } - } - /// # Safety - /// - /// - `slot` points to a `#ident` field of a pinned struct that this `__ThePinData` - /// describes. - /// - `slot` is valid and properly aligned. - /// - `*slot` is initialized, and the ownership is transferred to the returned - /// guard. - unsafe fn __project__pin( + /// - `slot` points to a `#field_name` field of a pinned struct that this + /// `__ThePinData` describes. + /// - `slot` is a valid, properly aligned and points to uninitialized and + /// exclusively accessed memory. + #[inline(always)] + unsafe fn _pin( self, slot: *mut PhantomPinned, - ) -> ::pin_init::__internal::DropGuard< + ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Pinned, PhantomPinned, > { - unsafe { ::pin_init::__internal::DropGuard::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(slot) } } } unsafe impl ::pin_init::__internal::HasPinData for Foo { From aa8b5987947fdca120486c04c22a886a964f2f43 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 25 Apr 2026 18:06:34 +0100 Subject: [PATCH 5/5] internal: project using full slot Instead of projecting using pointer to a field project the full slot. This further shifts the code generation from the initializer site to the struct definition site, which means less code is generated overall. It also makes the safety comment easier to justify, as now the projection is done by the `#[pin_data]` macro which has full visibility of pinnedness of fields. The field alignment could also be checked on the `#[pin_data]` side; however, since `init!()` macro works for other type of structs, we cannot remove the alignment check from `init!`/`pin_init!` side anyway, so I opted to still keep the alignment check in init.rs. Signed-off-by: Gary Guo --- internal/src/init.rs | 5 ++-- internal/src/pin_data.rs | 12 ++++---- src/lib.rs | 2 +- tests/ui/expand/many_generics.expanded.rs | 36 +++++++++++------------ tests/ui/expand/pin-data.expanded.rs | 24 +++++++-------- tests/ui/expand/pinned_drop.expanded.rs | 24 +++++++-------- 6 files changed, 51 insertions(+), 52 deletions(-) diff --git a/internal/src/init.rs b/internal/src/init.rs index e6f5ea06..699b1055 100644 --- a/internal/src/init.rs +++ b/internal/src/init.rs @@ -245,12 +245,11 @@ fn init_fields( let slot = if pinned { quote! { // SAFETY: - // - `&raw mut (*slot).#ident` points to the `#ident` field of `slot`. - // - `&raw mut (*slot).#ident` is valid. + // - `slot` is valid and properly aligned. // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. // - `make_field_check` prevents `#ident` from being used twice, therefore // `(*slot).#ident` is exclusively accessed and has not been initialized. - (unsafe { #data.#ident(&raw mut (*#slot).#ident) }) + (unsafe { #data.#ident(#slot) }) } } else { quote! { diff --git a/internal/src/pin_data.rs b/internal/src/pin_data.rs index 926f84a0..2284256a 100644 --- a/internal/src/pin_data.rs +++ b/internal/src/pin_data.rs @@ -377,21 +377,21 @@ fn generate_the_pin_data( quote! { /// # Safety /// - /// - `slot` points to a `#field_name` field of a pinned struct that this - /// `__ThePinData` describes. - /// - `slot` is a valid, properly aligned and points to uninitialized and - /// exclusively accessed memory. + /// - `slot` is valid and properly aligned. + /// - `(*slot).#field_name` is properly aligned. + /// - `(*slot).#field_name` points to uninitialized and exclusively accessed + /// memory. #(#attrs)* #[inline(always)] #vis unsafe fn #field_name( self, - slot: *mut #ty, + slot: *mut #struct_name #ty_generics, ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> { // SAFETY: // - If `#pin_marker` is `Pinned`, the corresponding field is structurally // pinned. // - Other safety requirements follows the safety requirement. - unsafe { ::pin_init::__internal::Slot::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#field_name) } } } }) diff --git a/src/lib.rs b/src/lib.rs index e891d65c..c9e2cbe2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -868,7 +868,7 @@ pub use pin_init_internal::init; macro_rules! assert_pinned { ($ty:ty, $field:ident, $field_ty:ty, inline) => { // SAFETY: This code is unreachable. - let _ = move |ptr: *mut $field_ty| unsafe { + let _ = move |ptr: *mut $ty| unsafe { let data = <$ty as $crate::__internal::HasPinData>::__pin_data(); _ = data .$field(ptr) diff --git a/tests/ui/expand/many_generics.expanded.rs b/tests/ui/expand/many_generics.expanded.rs index 45e8873d..1b0642fb 100644 --- a/tests/ui/expand/many_generics.expanded.rs +++ b/tests/ui/expand/many_generics.expanded.rs @@ -94,51 +94,51 @@ const _: () = { } /// # Safety /// - /// - `slot` points to a `#field_name` field of a pinned struct that this - /// `__ThePinData` describes. - /// - `slot` is a valid, properly aligned and points to uninitialized and - /// exclusively accessed memory. + /// - `slot` is valid and properly aligned. + /// - `(*slot).#field_name` is properly aligned. + /// - `(*slot).#field_name` points to uninitialized and exclusively accessed + /// memory. #[inline(always)] unsafe fn array( self, - slot: *mut [u8; 1024 * 1024], + slot: *mut Foo<'a, 'b, T, SIZE>, ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Unpinned, [u8; 1024 * 1024], > { - unsafe { ::pin_init::__internal::Slot::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).array) } } /// # Safety /// - /// - `slot` points to a `#field_name` field of a pinned struct that this - /// `__ThePinData` describes. - /// - `slot` is a valid, properly aligned and points to uninitialized and - /// exclusively accessed memory. + /// - `slot` is valid and properly aligned. + /// - `(*slot).#field_name` is properly aligned. + /// - `(*slot).#field_name` points to uninitialized and exclusively accessed + /// memory. #[inline(always)] unsafe fn r( self, - slot: *mut &'b mut [&'a mut T; SIZE], + slot: *mut Foo<'a, 'b, T, SIZE>, ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Unpinned, &'b mut [&'a mut T; SIZE], > { - unsafe { ::pin_init::__internal::Slot::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).r) } } /// # Safety /// - /// - `slot` points to a `#field_name` field of a pinned struct that this - /// `__ThePinData` describes. - /// - `slot` is a valid, properly aligned and points to uninitialized and - /// exclusively accessed memory. + /// - `slot` is valid and properly aligned. + /// - `(*slot).#field_name` is properly aligned. + /// - `(*slot).#field_name` points to uninitialized and exclusively accessed + /// memory. #[inline(always)] unsafe fn _pin( self, - slot: *mut PhantomPinned, + slot: *mut Foo<'a, 'b, T, SIZE>, ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Pinned, PhantomPinned, > { - unsafe { ::pin_init::__internal::Slot::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot)._pin) } } } unsafe impl< diff --git a/tests/ui/expand/pin-data.expanded.rs b/tests/ui/expand/pin-data.expanded.rs index 5b490d99..e79642cd 100644 --- a/tests/ui/expand/pin-data.expanded.rs +++ b/tests/ui/expand/pin-data.expanded.rs @@ -58,35 +58,35 @@ const _: () = { } /// # Safety /// - /// - `slot` points to a `#field_name` field of a pinned struct that this - /// `__ThePinData` describes. - /// - `slot` is a valid, properly aligned and points to uninitialized and - /// exclusively accessed memory. + /// - `slot` is valid and properly aligned. + /// - `(*slot).#field_name` is properly aligned. + /// - `(*slot).#field_name` points to uninitialized and exclusively accessed + /// memory. #[inline(always)] unsafe fn array( self, - slot: *mut [u8; 1024 * 1024], + slot: *mut Foo, ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Unpinned, [u8; 1024 * 1024], > { - unsafe { ::pin_init::__internal::Slot::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).array) } } /// # Safety /// - /// - `slot` points to a `#field_name` field of a pinned struct that this - /// `__ThePinData` describes. - /// - `slot` is a valid, properly aligned and points to uninitialized and - /// exclusively accessed memory. + /// - `slot` is valid and properly aligned. + /// - `(*slot).#field_name` is properly aligned. + /// - `(*slot).#field_name` points to uninitialized and exclusively accessed + /// memory. #[inline(always)] unsafe fn _pin( self, - slot: *mut PhantomPinned, + slot: *mut Foo, ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Pinned, PhantomPinned, > { - unsafe { ::pin_init::__internal::Slot::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot)._pin) } } } unsafe impl ::pin_init::__internal::HasPinData for Foo { diff --git a/tests/ui/expand/pinned_drop.expanded.rs b/tests/ui/expand/pinned_drop.expanded.rs index 58dcf372..0f1893c2 100644 --- a/tests/ui/expand/pinned_drop.expanded.rs +++ b/tests/ui/expand/pinned_drop.expanded.rs @@ -58,35 +58,35 @@ const _: () = { } /// # Safety /// - /// - `slot` points to a `#field_name` field of a pinned struct that this - /// `__ThePinData` describes. - /// - `slot` is a valid, properly aligned and points to uninitialized and - /// exclusively accessed memory. + /// - `slot` is valid and properly aligned. + /// - `(*slot).#field_name` is properly aligned. + /// - `(*slot).#field_name` points to uninitialized and exclusively accessed + /// memory. #[inline(always)] unsafe fn array( self, - slot: *mut [u8; 1024 * 1024], + slot: *mut Foo, ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Unpinned, [u8; 1024 * 1024], > { - unsafe { ::pin_init::__internal::Slot::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).array) } } /// # Safety /// - /// - `slot` points to a `#field_name` field of a pinned struct that this - /// `__ThePinData` describes. - /// - `slot` is a valid, properly aligned and points to uninitialized and - /// exclusively accessed memory. + /// - `slot` is valid and properly aligned. + /// - `(*slot).#field_name` is properly aligned. + /// - `(*slot).#field_name` points to uninitialized and exclusively accessed + /// memory. #[inline(always)] unsafe fn _pin( self, - slot: *mut PhantomPinned, + slot: *mut Foo, ) -> ::pin_init::__internal::Slot< ::pin_init::__internal::Pinned, PhantomPinned, > { - unsafe { ::pin_init::__internal::Slot::new(slot) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot)._pin) } } } unsafe impl ::pin_init::__internal::HasPinData for Foo {