summaryrefslogtreecommitdiff
path: root/rust/pin-init
diff options
context:
space:
mode:
Diffstat (limited to 'rust/pin-init')
-rw-r--r--rust/pin-init/README.md5
-rw-r--r--rust/pin-init/examples/error.rs2
-rw-r--r--rust/pin-init/examples/linked_list.rs1
-rw-r--r--rust/pin-init/examples/mutex.rs14
-rw-r--r--rust/pin-init/examples/pthread_mutex.rs3
-rw-r--r--rust/pin-init/examples/static_init.rs13
-rw-r--r--rust/pin-init/internal/src/diagnostics.rs14
-rw-r--r--rust/pin-init/internal/src/init.rs259
-rw-r--r--rust/pin-init/internal/src/lib.rs1
-rw-r--r--rust/pin-init/internal/src/pin_data.rs324
-rw-r--r--rust/pin-init/internal/src/zeroable.rs2
-rw-r--r--rust/pin-init/src/__internal.rs270
-rw-r--r--rust/pin-init/src/alloc.rs10
-rw-r--r--rust/pin-init/src/lib.rs477
14 files changed, 780 insertions, 615 deletions
diff --git a/rust/pin-init/README.md b/rust/pin-init/README.md
index 6cee6ab1eb57..2312c9e75f8c 100644
--- a/rust/pin-init/README.md
+++ b/rust/pin-init/README.md
@@ -3,7 +3,7 @@
[![Dependency status](https://deps.rs/repo/github/Rust-for-Linux/pin-init/status.svg)](https://deps.rs/repo/github/Rust-for-Linux/pin-init)
![License](https://img.shields.io/crates/l/pin-init)
[![Toolchain](https://img.shields.io/badge/toolchain-nightly-red)](#nightly-only)
-![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/Rust-for-Linux/pin-init/test.yml)
+![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/Rust-for-Linux/pin-init/ci.yml)
# `pin-init`
> [!NOTE]
@@ -160,7 +160,6 @@ actually does the initialization in the correct way. Here are the things to look
```rust
use pin_init::{pin_data, pinned_drop, PinInit, PinnedDrop, pin_init_from_closure};
use core::{
- ptr::addr_of_mut,
marker::PhantomPinned,
cell::UnsafeCell,
pin::Pin,
@@ -199,7 +198,7 @@ impl RawFoo {
unsafe {
pin_init_from_closure(move |slot: *mut Self| {
// `slot` contains uninit memory, avoid creating a reference.
- let foo = addr_of_mut!((*slot).foo);
+ let foo = &raw mut (*slot).foo;
let foo = UnsafeCell::raw_get(foo).cast::<bindings::foo>();
// Initialize the `foo`
diff --git a/rust/pin-init/examples/error.rs b/rust/pin-init/examples/error.rs
index 8f4e135eb8ba..96f095398e8d 100644
--- a/rust/pin-init/examples/error.rs
+++ b/rust/pin-init/examples/error.rs
@@ -11,6 +11,7 @@ use std::alloc::AllocError;
pub struct Error;
impl From<Infallible> for Error {
+ #[inline]
fn from(e: Infallible) -> Self {
match e {}
}
@@ -18,6 +19,7 @@ impl From<Infallible> for Error {
#[cfg(feature = "alloc")]
impl From<AllocError> for Error {
+ #[inline]
fn from(_: AllocError) -> Self {
Self
}
diff --git a/rust/pin-init/examples/linked_list.rs b/rust/pin-init/examples/linked_list.rs
index 8445a5890cb7..424585fe226d 100644
--- a/rust/pin-init/examples/linked_list.rs
+++ b/rust/pin-init/examples/linked_list.rs
@@ -2,7 +2,6 @@
#![allow(clippy::undocumented_unsafe_blocks)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
-#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
use core::{
cell::Cell,
diff --git a/rust/pin-init/examples/mutex.rs b/rust/pin-init/examples/mutex.rs
index 9f295226cd64..e8d4dbb664fe 100644
--- a/rust/pin-init/examples/mutex.rs
+++ b/rust/pin-init/examples/mutex.rs
@@ -2,7 +2,6 @@
#![allow(clippy::undocumented_unsafe_blocks)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
-#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
#![allow(clippy::missing_safety_doc)]
use core::{
@@ -80,11 +79,7 @@ impl<T> CMutex<T> {
wait_list <- ListHead::new(),
spin_lock: SpinLock::new(),
locked: Cell::new(false),
- data <- unsafe {
- pin_init_from_closure(|slot: *mut UnsafeCell<T>| {
- val.__pinned_init(slot.cast::<T>())
- })
- },
+ data <- UnsafeCell::pin_init(val),
})
}
@@ -92,7 +87,7 @@ impl<T> CMutex<T> {
pub fn lock(&self) -> Pin<CMutexGuard<'_, T>> {
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);
@@ -100,9 +95,6 @@ impl<T> CMutex<T> {
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 {
@@ -219,7 +211,7 @@ fn main() {
for h in handles {
h.join().expect("thread panicked");
}
- println!("{:?}", &*mtx.lock());
+ println!("{:?}", *mtx.lock());
assert_eq!(*mtx.lock(), workload * thread_count * 2);
}
}
diff --git a/rust/pin-init/examples/pthread_mutex.rs b/rust/pin-init/examples/pthread_mutex.rs
index 4e082ec7d5de..00f457e68827 100644
--- a/rust/pin-init/examples/pthread_mutex.rs
+++ b/rust/pin-init/examples/pthread_mutex.rs
@@ -3,7 +3,6 @@
// inspired by <https://github.com/nbdd0121/pin-init/blob/trunk/examples/pthread_mutex.rs>
#![allow(clippy::undocumented_unsafe_blocks)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
-#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
#[cfg(not(windows))]
mod pthread_mtx {
@@ -178,7 +177,7 @@ fn main() {
for h in handles {
h.join().expect("thread panicked");
}
- println!("{:?}", &*mtx.lock());
+ println!("{:?}", *mtx.lock());
assert_eq!(*mtx.lock(), workload * thread_count * 2);
}
}
diff --git a/rust/pin-init/examples/static_init.rs b/rust/pin-init/examples/static_init.rs
index 0e165daa9798..8dd52313c1b8 100644
--- a/rust/pin-init/examples/static_init.rs
+++ b/rust/pin-init/examples/static_init.rs
@@ -2,7 +2,6 @@
#![allow(clippy::undocumented_unsafe_blocks)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
-#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
#![allow(unused_imports)]
use core::{
@@ -60,7 +59,7 @@ impl<T, I: PinInit<T>> ops::Deref for StaticInit<T, I> {
println!("doing init");
let ptr = self.cell.get().cast::<T>();
match self.init.take() {
- Some(f) => unsafe { f.__pinned_init(ptr).unwrap() },
+ Some(f) => unsafe { pin_init::raw_init(ptr, f) },
None => unsafe { core::hint::unreachable_unchecked() },
}
self.present.set(true);
@@ -72,13 +71,11 @@ impl<T, I: PinInit<T>> ops::Deref for StaticInit<T, I> {
pub struct CountInit;
unsafe impl PinInit<CMutex<usize>> for CountInit {
- unsafe fn __pinned_init(
- self,
- slot: *mut CMutex<usize>,
- ) -> Result<(), core::convert::Infallible> {
+ unsafe fn __init(self, slot: *mut CMutex<usize>) -> Result<(), core::convert::Infallible> {
let init = CMutex::new(0);
std::thread::sleep(std::time::Duration::from_millis(1000));
- unsafe { init.__pinned_init(slot) }
+ unsafe { pin_init::raw_init(slot, init) };
+ Ok(())
}
}
@@ -118,7 +115,7 @@ fn main() {
for h in handles {
h.join().expect("thread panicked");
}
- println!("{:?}, {:?}", &*mtx.lock(), &*COUNT.lock());
+ println!("{:?}, {:?}", *mtx.lock(), *COUNT.lock());
assert_eq!(*mtx.lock(), workload * thread_count * 2);
}
}
diff --git a/rust/pin-init/internal/src/diagnostics.rs b/rust/pin-init/internal/src/diagnostics.rs
index 3bdb477c2f2b..c7d9b3e624fc 100644
--- a/rust/pin-init/internal/src/diagnostics.rs
+++ b/rust/pin-init/internal/src/diagnostics.rs
@@ -3,6 +3,7 @@
use std::fmt::Display;
use proc_macro2::TokenStream;
+use quote::quote_spanned;
use syn::{spanned::Spanned, Error};
pub(crate) struct DiagCtxt(TokenStream);
@@ -15,6 +16,19 @@ impl DiagCtxt {
ErrorGuaranteed(())
}
+ pub(crate) fn warn(&mut self, span: impl Spanned, msg: impl Display) {
+ // Have the message start on a new line for visual clarity.
+ let msg = format!("\n{}", msg);
+ self.0.extend(quote_spanned!(span.span() =>
+ // Approximate using deprecated warning while `proc_macro_diagnostic` is unstable.
+ const _: () = {
+ #[deprecated = #msg]
+ const fn warn() {}
+ warn();
+ };
+ ));
+ }
+
pub(crate) fn with(
fun: impl FnOnce(&mut DiagCtxt) -> Result<TokenStream, ErrorGuaranteed>,
) -> TokenStream {
diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs
index 2fe918f4d82a..fd0b5ea4a0a3 100644
--- a/rust/pin-init/internal/src/init.rs
+++ b/rust/pin-init/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},
@@ -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
@@ -172,8 +169,7 @@ pub(crate) fn expand(
init(slot).map(|__InitOk| ())
};
// SAFETY: TODO
- let init = unsafe { ::pin_init::#init_from_closure::<_, #error>(init) };
- init
+ unsafe { ::pin_init::#init_from_closure::<_, #error>(init) }
}})
}
@@ -232,123 +228,84 @@ 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, .. } => {
+ let stmt = &block.stmts;
+ res.extend(quote! {
+ #(#attrs)*
+ {
+ #(#stmt)*
+ }
+ });
+ continue;
+ }
+ };
+
+ let slot = if pinned {
+ quote! {
+ // SAFETY:
+ // - `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(#slot) })
+ }
+ } 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.
+ // - `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 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);
- // NOTE: the field accessor ensures that the initialized field is properly aligned.
- // Unaligned fields will cause the compiler to emit E0793. We do not support
- // unaligned fields since `Init::__init` requires an aligned pointer; the call to
- // `ptr::write` below has the same requirement.
- let accessor = if pinned {
- let project_ident = format_ident!("__project_{ident}");
- quote! {
- // SAFETY: TODO
- unsafe { #data.#project_ident(&mut (*#slot).#ident) }
- }
- } else {
- quote! {
- // SAFETY: TODO
- unsafe { &mut (*#slot).#ident }
- }
- };
+ let value = value
+ .as_ref()
+ .map(|(_, value)| quote!(#value))
+ .unwrap_or_else(|| quote!(#ident));
+
quote! {
#(#attrs)*
- {
- #value_prep
- // SAFETY: TODO
- unsafe { #write(::core::ptr::addr_of_mut!((*#slot).#ident), #value_ident) };
- }
- #(#cfgs)*
- #[allow(unused_variables)]
- let #ident = #accessor;
+ let mut #guard = #slot.write(#value);
+
}
}
- InitializerKind::Init { ident, value, .. } => {
- // Again span for better diagnostics
- let init = format_ident!("init", span = value.span());
- // NOTE: the field accessor ensures that the initialized field is properly aligned.
- // Unaligned fields will cause the compiler to emit E0793. We do not support
- // unaligned fields since `Init::__init` requires an aligned pointer; the call to
- // `ptr::write` below has the same requirement.
- let (value_init, accessor) = if pinned {
- let project_ident = format_ident!("__project_{ident}");
- (
- 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(::core::ptr::addr_of_mut!((*#slot).#ident), #init)? };
- },
- quote! {
- // SAFETY: TODO
- unsafe { #data.#project_ident(&mut (*#slot).#ident) }
- },
- )
- } 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,
- ::core::ptr::addr_of_mut!((*#slot).#ident),
- )?
- };
- },
- quote! {
- // SAFETY: TODO
- unsafe { &mut (*#slot).#ident }
- },
- )
- };
+ InitializerKind::Init { value, .. } => {
quote! {
#(#attrs)*
- {
- let #init = #value;
- #value_init
- }
- #(#cfgs)*
- #[allow(unused_variables)]
- let #ident = #accessor;
+ let mut #guard = #slot.init(#value)?;
}
}
- 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());
- res.extend(quote! {
- #(#cfgs)*
- // Create the drop guard:
- //
- // We rely on macro hygiene to make it impossible for users to access this local
- // variable.
- // SAFETY: We forget the guard later when initialization has succeeded.
- let #guard = unsafe {
- ::pin_init::__internal::DropGuard::new(
- ::core::ptr::addr_of_mut!((*slot).#ident)
- )
- };
- });
- guards.push(guard);
- guard_attrs.push(cfgs);
- }
+
+ res.extend(quote! {
+ #init
+
+ #(#cfgs)*
+ // Allow `non_snake_case` since the same warning is going to be reported for the struct
+ // field.
+ #[allow(unused_variables, non_snake_case)]
+ let #ident = #guard.let_binding();
+ });
+
+ guards.push(guard);
+ guard_attrs.push(cfgs);
}
quote! {
#res
@@ -361,49 +318,49 @@ fn init_fields(
}
}
-/// Generate the check for ensuring that every field has been initialized.
+/// Generate the check for ensuring that every field has been initialized and aligned.
fn make_field_check(
fields: &Punctuated<InitializerField, Token![,]>,
init_kind: InitKind,
path: &Path,
) -> TokenStream {
- let field_attrs = fields
+ let field_attrs: Vec<_> = fields
.iter()
- .filter_map(|f| f.kind.ident().map(|_| &f.attrs));
- let field_name = fields.iter().filter_map(|f| f.kind.ident());
- match init_kind {
- InitKind::Normal => quote! {
- // We use unreachable code to ensure that all fields have been mentioned exactly once,
- // this struct initializer will still be type-checked and complain with a very natural
- // error message if a field is forgotten/mentioned more than once.
- #[allow(unreachable_code, clippy::diverging_sub_expression)]
- // SAFETY: this code is never executed.
- let _ = || unsafe {
- ::core::ptr::write(slot, #path {
- #(
- #(#field_attrs)*
- #field_name: ::core::panic!(),
- )*
- })
- };
- },
- InitKind::Zeroing => quote! {
- // We use unreachable code to ensure that all fields have been mentioned at most once.
- // Since the user specified `..Zeroable::zeroed()` at the end, all missing fields will
- // be zeroed. This struct initializer will still be type-checked and complain with a
- // very natural error message if a field is mentioned more than once, or doesn't exist.
- #[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)]
- // SAFETY: this code is never executed.
- let _ = || unsafe {
- ::core::ptr::write(slot, #path {
- #(
- #(#field_attrs)*
- #field_name: ::core::panic!(),
- )*
- ..::core::mem::zeroed()
- })
- };
- },
+ .filter_map(|f| f.kind.ident().map(|_| &f.attrs))
+ .collect();
+ let field_name: Vec<_> = fields.iter().filter_map(|f| f.kind.ident()).collect();
+ let zeroing_trailer = match init_kind {
+ InitKind::Normal => None,
+ InitKind::Zeroing => Some(quote! {
+ ..::core::mem::zeroed()
+ }),
+ };
+ quote! {
+ #[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 {
+ // Create references to ensure that the initialized field is properly aligned.
+ // Unaligned fields will cause the compiler to emit E0793. We do not support
+ // unaligned fields since `Init::__init` requires an aligned pointer; the call to
+ // `ptr::write` for value-initialization case has the same requirement.
+ #(
+ #(#field_attrs)*
+ let _ = &(*slot).#field_name;
+ )*
+
+ // If the zeroing trailer is not present, this checks that all fields have been
+ // mentioned exactly once. If the zeroing trailer is present, all missing fields will be
+ // zeroed, so this checks that all fields have been mentioned at most once. The use of
+ // struct initializer will still generate very natural error messages for any misuse.
+ ::core::ptr::write(slot, #path {
+ #(
+ #(#field_attrs)*
+ #field_name: loop {},
+ )*
+ #zeroing_trailer
+ })
+ };
}
}
diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index 08372c8f65f0..60d5093f3128 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -6,7 +6,6 @@
//! `pin-init` proc macros.
-#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
// Documentation is done in the pin-init crate instead.
#![allow(missing_docs)]
diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs
index 7d871236b49c..ff194d27565e 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,
- 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,6 +35,20 @@ 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,
+}
+
pub(crate) fn pin_data(
args: Args,
input: Item,
@@ -62,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::<TokenStream>()
+ .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.
@@ -73,24 +136,38 @@ pub(crate) fn pin_data(
replacer.visit_generics_mut(&mut struct_.generics);
replacer.visit_fields_mut(&mut struct_.fields);
- let fields: Vec<(bool, &Field)> = struct_
+ let fields: Vec<FieldInfo<'_>> = struct_
.fields
.iter_mut()
.map(|field| {
let len = field.attrs.len();
field.attrs.retain(|a| !a.path().is_ident("pin"));
- (len != field.attrs.len(), &*field)
+ let pinned_count = len - field.attrs.len();
+ if pinned_count > 1 {
+ dcx.error(&field, "#[pin] attribute specified more than once");
+ }
+
+ 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,
+ }
})
.collect();
- for (pinned, field) in &fields {
- if !pinned && is_phantom_pinned(&field.ty) {
- dcx.error(
- field,
+ for field in &fields {
+ let ident = field.field.ident.as_ref().unwrap();
+
+ if !field.pinned && is_phantom_pinned(&field.field.ty) {
+ dcx.warn(
+ field.field,
format!(
- "The field `{}` of type `PhantomPinned` only has an effect \
+ "The field `{ident}` of type `PhantomPinned` only has an effect \
if it has the `#[pin]` attribute",
- field.ident.as_ref().unwrap(),
),
);
}
@@ -143,7 +220,7 @@ fn is_phantom_pinned(ty: &Type) -> bool {
fn generate_unpin_impl(
ident: &Ident,
generics: &Generics,
- fields: &[(bool, &Field)],
+ fields: &[FieldInfo<'_>],
) -> TokenStream {
let (_, ty_generics, _) = generics.split_for_impl();
let mut generics_with_pin_lt = generics.clone();
@@ -160,19 +237,26 @@ fn generate_unpin_impl(
else {
unreachable!()
};
- let pinned_fields = fields.iter().filter_map(|(b, f)| b.then_some(f));
+ let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| {
+ let ident = f.field.ident.as_ref().unwrap();
+ let ty = &f.field.ty;
+ quote!(
+ #ident: #ty
+ )
+ });
quote! {
// This struct will be used for the unpin analysis. It is needed, because only structurally
// pinned fields are relevant whether the struct should implement `Unpin`.
- #[allow(dead_code)] // The fields below are never used.
+ #[allow(
+ dead_code, // The fields below are never used.
+ non_snake_case // The warning will be emitted on the struct definition.
+ )]
struct __Unpin #generics_with_pin_lt
#where_token
#predicates
{
- __phantom_pin: ::core::marker::PhantomData<fn(&'__pin ()) -> &'__pin ()>,
- __phantom: ::core::marker::PhantomData<
- fn(#ident #ty_generics) -> #ident #ty_generics
- >,
+ __phantom_pin: ::pin_init::__internal::PhantomInvariantLifetime<'__pin>,
+ __phantom: ::pin_init::__internal::PhantomInvariant<#ident #ty_generics>,
#(#pinned_fields),*
}
@@ -214,7 +298,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<T: ::core::ops::Drop + ?::core::marker::Sized> MustNotImplDrop for T {}
impl #impl_generics MustNotImplDrop for #ident #ty_generics
#whr
@@ -222,7 +305,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<T: ::pin_init::PinnedDrop + ?::core::marker::Sized>
UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
@@ -238,7 +320,7 @@ fn generate_projections(
vis: &Visibility,
ident: &Ident,
generics: &Generics,
- fields: &[(bool, &Field)],
+ fields: &[FieldInfo<'_>],
) -> TokenStream {
let (impl_generics, ty_generics, _) = generics.split_for_impl();
let mut generics_with_pin_lt = generics.clone();
@@ -247,32 +329,20 @@ fn generate_projections(
let projection = format_ident!("{ident}Projection");
let this = format_ident!("this");
- let (fields_decl, fields_proj) = collect_tuple(fields.iter().map(
- |(
- pinned,
- Field {
- vis,
- ident,
- ty,
- attrs,
- ..
- },
- )| {
- let mut attrs = attrs.clone();
- attrs.retain(|a| !a.path().is_ident("pin"));
- let mut no_doc_attrs = attrs.clone();
- no_doc_attrs.retain(|a| !a.path().is_ident("doc"));
+ let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields
+ .iter()
+ .map(|field| {
+ let Field { vis, ident, ty, .. } = &field.field;
+
let ident = ident
.as_ref()
.expect("only structs with named fields are supported");
- if *pinned {
+ if field.pinned {
(
quote!(
- #(#attrs)*
#vis #ident: ::core::pin::Pin<&'__pin mut #ty>,
),
quote!(
- #(#no_doc_attrs)*
// SAFETY: this field is structurally pinned.
#ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },
),
@@ -280,31 +350,33 @@ fn generate_projections(
} else {
(
quote!(
- #(#attrs)*
#vis #ident: &'__pin mut #ty,
),
quote!(
- #(#no_doc_attrs)*
#ident: &mut #this.#ident,
),
)
}
- },
- ));
+ })
+ .collect();
let structurally_pinned_fields_docs = fields
.iter()
- .filter_map(|(pinned, field)| pinned.then_some(field))
- .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap()));
+ .filter(|f| f.pinned)
+ .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
let not_structurally_pinned_fields_docs = fields
.iter()
- .filter_map(|(pinned, field)| (!pinned).then_some(field))
- .map(|Field { ident, .. }| format!(" - `{}`", ident.as_ref().unwrap()));
+ .filter(|f| !f.pinned)
+ .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap()));
let docs = format!(" Pin-projections of [`{ident}`]");
quote! {
#[doc = #docs]
- #[allow(dead_code)]
+ // Allow `non_snake_case` since the same warning will be emitted on
+ // the struct definition.
+ #[allow(dead_code, non_snake_case)]
#[doc(hidden)]
- #vis struct #projection #generics_with_pin_lt {
+ #vis struct #projection #generics_with_pin_lt
+ #whr
+ {
#(#fields_decl)*
___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
}
@@ -336,91 +408,52 @@ fn generate_projections(
fn generate_the_pin_data(
vis: &Visibility,
- ident: &Ident,
+ struct_name: &Ident,
generics: &Generics,
- fields: &[(bool, &Field)],
+ fields: &[FieldInfo<'_>],
) -> TokenStream {
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.
- fn handle_field(
- Field {
- vis,
- ident,
- ty,
- attrs,
- ..
- }: &Field,
- struct_ident: &Ident,
- pinned: bool,
- ) -> TokenStream {
- let mut attrs = attrs.clone();
- attrs.retain(|a| !a.path().is_ident("pin"));
- let ident = ident
- .as_ref()
- .expect("only structs with named fields are supported");
- let project_ident = format_ident!("__project_{ident}");
- let (init_ty, init_fn, project_ty, project_body, pin_safety) = if 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!(
- /// - `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!(),
- )
- };
- let slot_safety = format!(
- " `slot` points at the field `{ident}` inside of `{struct_ident}`, which is pinned.",
- );
- 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 #ident<E>(
- 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
- ///
- #[doc = #slot_safety]
- #(#attrs)*
- #vis unsafe fn #project_ident<'__slot>(
- self,
- slot: &'__slot mut #ty,
- ) -> #project_ty {
- #project_body
- }
- }
- }
-
+ // 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(|(pinned, field)| handle_field(field, ident, *pinned))
+ .map(|f| {
+ let Field { vis, ident, ty, .. } = f.field;
+
+ let field_name = ident
+ .as_ref()
+ .expect("only structs with named fields are supported");
+ let pin_marker = if f.pinned {
+ quote!(Pinned)
+ } else {
+ quote!(Unpinned)
+ };
+ quote! {
+ /// # Safety
+ ///
+ /// - `slot` is valid and properly aligned.
+ /// - `(*slot).#field_name` is properly aligned.
+ /// - `(*slot).#field_name` points to uninitialized and exclusively accessed
+ /// memory.
+ // Allow `non_snake_case` since the same warning will be emitted on
+ // the struct definition.
+ #[allow(non_snake_case)]
+ #[inline(always)]
+ #vis unsafe fn #field_name(
+ self,
+ 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(&raw mut (*slot).#field_name) }
+ }
+ }
+ })
.collect::<TokenStream>();
quote! {
// We declare this struct which will host all of the projection function for our type. It
@@ -429,14 +462,13 @@ fn generate_the_pin_data(
#vis struct __ThePinData #generics
#whr
{
- __phantom: ::core::marker::PhantomData<
- fn(#ident #ty_generics) -> #ident #ty_generics
- >,
+ __phantom: ::pin_init::__internal::PhantomInvariant<#struct_name #ty_generics>,
}
impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics
#whr
{
+ #[inline]
fn clone(&self) -> Self { *self }
}
@@ -445,31 +477,34 @@ 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
{
+ /// 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
}
// SAFETY: We have added the correct projection functions above to `__ThePinData` and
// we also use the least restrictive generics possible.
- unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #ident #ty_generics
+ unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #struct_name #ty_generics
#whr
{
type PinData = __ThePinData #ty_generics;
+ #[inline]
unsafe fn __pin_data() -> Self::PinData {
- __ThePinData { __phantom: ::core::marker::PhantomData }
+ __ThePinData { __phantom: ::pin_init::__internal::PhantomInvariant::new() }
}
}
-
- // SAFETY: TODO
- unsafe impl #impl_generics ::pin_init::__internal::PinData for __ThePinData #ty_generics
- #whr
- {
- type Datee = #ident #ty_generics;
- }
}
}
@@ -500,14 +535,3 @@ impl VisitMut for SelfReplacer {
// Do not descend into items, since items reset/change what `Self` refers to.
}
}
-
-// replace with `.collect()` once MSRV is above 1.79
-fn collect_tuple<A, B>(iter: impl Iterator<Item = (A, B)>) -> (Vec<A>, Vec<B>) {
- let mut res_a = vec![];
- let mut res_b = vec![];
- for (a, b) in iter {
- res_a.push(a);
- res_b.push(b);
- }
- (res_a, res_b)
-}
diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/internal/src/zeroable.rs
index 05683319b0f7..b11feaeb1ca6 100644
--- a/rust/pin-init/internal/src/zeroable.rs
+++ b/rust/pin-init/internal/src/zeroable.rs
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: Apache-2.0 OR MIT
use proc_macro2::TokenStream;
use quote::quote;
diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs
index 90adbdc1893b..8e9fd18b993f 100644
--- a/rust/pin-init/src/__internal.rs
+++ b/rust/pin-init/src/__internal.rs
@@ -7,42 +7,54 @@
use super::*;
-/// See the [nomicon] for what subtyping is. See also [this table].
+/// Zero-sized type used to mark a type as invariant.
+///
+/// This is a polyfill for the [unstable type] in the standard library of the same name.
///
-/// The reason for not using `PhantomData<*mut T>` is that that type never implements [`Send`] and
-/// [`Sync`]. Hence `fn(*mut T) -> *mut T` is used, as that type always implements them.
+/// See the [nomicon] for what subtyping is. See also [this table].
///
+/// [unstable type]: https://doc.rust-lang.org/nightly/std/marker/struct.PhantomInvariant.html
/// [nomicon]: https://doc.rust-lang.org/nomicon/subtyping.html
/// [this table]: https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns
-pub(crate) type Invariant<T> = PhantomData<fn(*mut T) -> *mut T>;
+#[repr(transparent)]
+pub struct PhantomInvariant<T: ?Sized>(PhantomData<fn(T) -> T>);
-/// Module-internal type implementing `PinInit` and `Init`.
-///
-/// It is unsafe to create this type, since the closure needs to fulfill the same safety
-/// requirement as the `__pinned_init`/`__init` functions.
-pub(crate) struct InitClosure<F, T: ?Sized, E>(pub(crate) F, pub(crate) Invariant<(E, T)>);
-
-// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the
-// `__init` invariants.
-unsafe impl<T: ?Sized, F, E> Init<T, E> for InitClosure<F, T, E>
-where
- F: FnOnce(*mut T) -> Result<(), E>,
-{
- #[inline]
- unsafe fn __init(self, slot: *mut T) -> Result<(), E> {
- (self.0)(slot)
+impl<T: ?Sized> Clone for PhantomInvariant<T> {
+ #[inline(always)]
+ fn clone(&self) -> Self {
+ *self
}
}
-// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the
-// `__pinned_init` invariants.
-unsafe impl<T: ?Sized, F, E> PinInit<T, E> for InitClosure<F, T, E>
-where
- F: FnOnce(*mut T) -> Result<(), E>,
-{
- #[inline]
- unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
- (self.0)(slot)
+impl<T: ?Sized> Copy for PhantomInvariant<T> {}
+
+impl<T: ?Sized> Default for PhantomInvariant<T> {
+ #[inline(always)]
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<T: ?Sized> PhantomInvariant<T> {
+ #[inline(always)]
+ pub const fn new() -> Self {
+ Self(PhantomData)
+ }
+}
+
+/// Zero-sized type used to mark a lifetime as invariant.
+///
+/// This is a polyfill for the [unstable type] in the standard library of the same name.
+///
+/// [unstable type]: https://doc.rust-lang.org/nightly/std/marker/struct.PhantomInvariantLifetime.html
+#[repr(transparent)]
+#[derive(Clone, Copy, Default)]
+pub struct PhantomInvariantLifetime<'a>(PhantomInvariant<&'a ()>);
+
+impl PhantomInvariantLifetime<'_> {
+ #[inline(always)]
+ pub const fn new() -> Self {
+ Self(PhantomInvariant::new())
}
}
@@ -71,30 +83,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<F, E>(self, f: F) -> F
- where
- F: FnOnce(*mut Self::Datee) -> Result<InitOk, E>,
- {
- f
- }
-}
-
/// This trait is automatically implemented for every type. It aims to provide the same type
/// inference help as `HasPinData`.
///
@@ -102,33 +96,16 @@ 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<F, E>(self, f: F) -> F
- where
- F: FnOnce(*mut Self::Datee) -> Result<InitOk, E>,
- {
- f
- }
-}
-
-pub struct AllData<T: ?Sized>(Invariant<T>);
+pub struct AllData<T: ?Sized>(PhantomInvariant<T>);
impl<T: ?Sized> Clone for AllData<T> {
+ #[inline]
fn clone(&self) -> Self {
*self
}
@@ -136,17 +113,24 @@ impl<T: ?Sized> Clone for AllData<T> {
impl<T: ?Sized> Copy for AllData<T> {}
-// SAFETY: TODO.
-unsafe impl<T: ?Sized> InitData for AllData<T> {
- type Datee = T;
+impl<T: ?Sized> AllData<T> {
+ /// Type inference helper function.
+ #[inline(always)]
+ pub fn __make_closure<F, E>(self, f: F) -> F
+ where
+ F: FnOnce(*mut T) -> Result<InitOk, E>,
+ {
+ f
+ }
}
// SAFETY: TODO.
unsafe impl<T: ?Sized> HasInitData for T {
type InitData = AllData<T>;
+ #[inline]
unsafe fn __init_data() -> Self::InitData {
- AllData(PhantomData)
+ AllData(PhantomInvariant::new())
}
}
@@ -199,7 +183,7 @@ impl<T> StackInit<T> {
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`.
@@ -235,35 +219,144 @@ fn stack_init_reuse() {
println!("{value:?}");
}
+// Marker types that determines type of `DropGuard`'s let bindings.
+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<P, T: ?Sized> {
+ ptr: *mut T,
+ _phantom: PhantomData<P>,
+}
+
+impl<P, T: ?Sized> Slot<P, T> {
+ /// # 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<P, T>
+ 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<T: ?Sized> Slot<Unpinned, T> {
+ /// Initialize the field.
+ #[inline(always)]
+ pub fn init<E>(self, init: impl Init<T, E>) -> Result<DropGuard<Unpinned, T>, 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<T: ?Sized> Slot<Pinned, T> {
+ /// Initialize the field.
+ #[inline(always)]
+ pub fn init<E>(self, init: impl PinInit<T, E>) -> Result<DropGuard<Pinned, T>, 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.__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.
-pub struct DropGuard<T: ?Sized> {
+///
+/// # Invariants
+///
+/// - `ptr` is valid and properly aligned.
+/// - `*ptr` is initialized and owned by this guard.
+/// - if `P` is `Pinned`, `ptr` is pinned.
+pub struct DropGuard<P, T: ?Sized> {
ptr: *mut T,
+ phantom: PhantomData<P>,
}
-impl<T: ?Sized> DropGuard<T> {
- /// Creates a new [`DropGuard<T>`]. It will [`ptr::drop_in_place`] `ptr` when it gets dropped.
+impl<P, T: ?Sized> DropGuard<P, T> {
+ /// Creates a drop guard and transfer the ownership of the pointer content.
///
- /// # Safety
+ /// The ownership is only relinguished if the guard is forgotten via [`core::mem::forget`].
///
- /// `ptr` must be a valid pointer.
+ /// # Safety
///
- /// It is the callers responsibility that `self` will only get dropped if the pointee of `ptr`:
- /// - has not been dropped,
- /// - is not accessible by any other means,
- /// - will not be dropped by any other means.
+ /// - `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 {
- Self { ptr }
+ // INVARIANT: By safety requirement.
+ Self {
+ ptr,
+ phantom: PhantomData,
+ }
}
}
-impl<T: ?Sized> Drop for DropGuard<T> {
+impl<T: ?Sized> DropGuard<Unpinned, T> {
+ /// Create a let binding for accessor use.
+ #[inline]
+ pub fn let_binding(&mut self) -> &mut T {
+ // SAFETY: Per type invariant.
+ unsafe { &mut *self.ptr }
+ }
+}
+
+impl<T: ?Sized> DropGuard<Pinned, T> {
+ /// 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<P, T: ?Sized> Drop for DropGuard<P, T> {
#[inline]
fn drop(&mut self) {
- // SAFETY: A `DropGuard` can only be constructed using the unsafe `new` function
- // ensuring that this operation is safe.
+ // SAFETY: `self.ptr` is valid, properly aligned and `*self.ptr` is owned by this guard.
unsafe { ptr::drop_in_place(self.ptr) }
}
}
@@ -294,20 +387,23 @@ pub struct AlwaysFail<T: ?Sized> {
impl<T: ?Sized> AlwaysFail<T> {
/// Creates a new initializer that always fails.
+ #[inline]
pub fn new() -> Self {
Self { _t: PhantomData }
}
}
impl<T: ?Sized> Default for AlwaysFail<T> {
+ #[inline]
fn default() -> Self {
Self::new()
}
}
-// SAFETY: `__pinned_init` always fails, which is always okay.
+// SAFETY: `__init` always fails, which is always okay.
unsafe impl<T: ?Sized> PinInit<T, ()> for AlwaysFail<T> {
- unsafe fn __pinned_init(self, _slot: *mut T) -> Result<(), ()> {
+ #[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 5017f57442d8..471652e8663a 100644
--- a/rust/pin-init/src/alloc.rs
+++ b/rust/pin-init/src/alloc.rs
@@ -35,10 +35,11 @@ pub trait InPlaceInit<T>: Sized {
/// type.
///
/// If `T: !Unpin` it will not be able to move afterwards.
+ #[inline]
fn pin_init(init: impl PinInit<T>) -> Result<Pin<Self>, 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 {},
})
@@ -52,6 +53,7 @@ pub trait InPlaceInit<T>: Sized {
E: From<AllocError>;
/// Use the given initializer to in-place initialize a `T`.
+ #[inline]
fn init(init: impl Init<T>) -> Result<Self, AllocError> {
// SAFETY: We delegate to `init` and only change the error type.
let init = unsafe {
@@ -109,7 +111,7 @@ impl<T> InPlaceInit<T> for Arc<T> {
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()) })
}
@@ -136,6 +138,7 @@ impl<T> InPlaceInit<T> for Arc<T> {
impl<T> InPlaceWrite<T> for Box<MaybeUninit<T>> {
type Initialized = Box<T>;
+ #[inline]
fn write_init<E>(mut self, init: impl Init<T, E>) -> Result<Self::Initialized, E> {
let slot = self.as_mut_ptr();
// SAFETY: When init errors/panics, slot will get deallocated but not dropped,
@@ -145,11 +148,12 @@ impl<T> InPlaceWrite<T> for Box<MaybeUninit<T>> {
Ok(unsafe { self.assume_init() })
}
+ #[inline]
fn write_pin_init<E>(mut self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, E> {
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 fe4c85ae3f02..f1463be9479d 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};
@@ -172,7 +170,6 @@
//! # #![feature(extern_types)]
//! use pin_init::{pin_data, pinned_drop, PinInit, PinnedDrop, pin_init_from_closure};
//! use core::{
-//! ptr::addr_of_mut,
//! marker::PhantomPinned,
//! cell::UnsafeCell,
//! pin::Pin,
@@ -211,7 +208,7 @@
//! unsafe {
//! pin_init_from_closure(move |slot: *mut Self| {
//! // `slot` contains uninit memory, avoid creating a reference.
-//! let foo = addr_of_mut!((*slot).foo);
+//! let foo = &raw mut (*slot).foo;
//! let foo = UnsafeCell::raw_get(foo).cast::<bindings::foo>();
//!
//! // Initialize the `foo`
@@ -264,14 +261,6 @@
//! [`impl Init<T, E>`]: crate::Init
//! [Rust-for-Linux]: https://rust-for-linux.com/
-#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
-#![cfg_attr(
- all(
- any(feature = "alloc", feature = "std"),
- not(RUSTC_NEW_UNINIT_IS_STABLE)
- ),
- feature(new_uninit)
-)]
#![forbid(missing_docs, unsafe_op_in_unsafe_fn)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
@@ -279,6 +268,8 @@
all(feature = "unsafe-pinned", CONFIG_RUSTC_HAS_UNSAFE_PINNED),
feature(unsafe_pinned)
)]
+#![cfg_attr(all(USE_RUSTC_FEATURES, doc), allow(internal_features))]
+#![cfg_attr(all(USE_RUSTC_FEATURES, doc), feature(rustdoc_internals))]
use core::{
cell::UnsafeCell,
@@ -438,7 +429,7 @@ pub use ::pin_init_internal::Zeroable;
/// ```
/// use pin_init::MaybeZeroable;
///
-/// // implmements `Zeroable`
+/// // implements `Zeroable`
/// #[derive(MaybeZeroable)]
/// pub struct DriverData {
/// pub(crate) id: i64,
@@ -446,7 +437,7 @@ pub use ::pin_init_internal::Zeroable;
/// len: usize,
/// }
///
-/// // does not implmement `Zeroable`
+/// // does not implement `Zeroable`
/// #[derive(MaybeZeroable)]
/// pub struct DriverData2 {
/// pub(crate) id: i64,
@@ -463,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::*;
@@ -500,13 +490,7 @@ macro_rules! stack_pin_init {
(let $var:ident $(: $t:ty)? = $val:expr) => {
let val = $val;
let mut $var = ::core::pin::pin!($crate::__internal::StackInit$(::<$t>)?::uninit());
- let mut $var = match $crate::__internal::StackInit::init($var, val) {
- Ok(res) => res,
- Err(x) => {
- let x: ::core::convert::Infallible = x;
- match x {}
- }
- };
+ let Ok(mut $var) = $crate::__internal::StackInit::init($var, val);
};
}
@@ -515,7 +499,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::*;
@@ -542,7 +525,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::*;
@@ -665,7 +647,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]
@@ -755,7 +736,7 @@ macro_rules! stack_try_pin_init {
///
/// ```rust
/// # use pin_init::*;
-/// # use core::{ptr::addr_of_mut, marker::PhantomPinned};
+/// # use core::marker::PhantomPinned;
/// #[pin_data]
/// #[derive(Zeroable)]
/// struct Buf {
@@ -769,7 +750,7 @@ macro_rules! stack_try_pin_init {
/// let init = pin_init!(&this in Buf {
/// buf: [0; 64],
/// // SAFETY: TODO.
-/// ptr: unsafe { addr_of_mut!((*this.as_ptr()).buf).cast() },
+/// ptr: unsafe { (&raw mut (*this.as_ptr()).buf).cast() },
/// pin: PhantomPinned,
/// });
/// let init = pin_init!(Buf {
@@ -874,12 +855,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 $ty| unsafe {
+ let data = <$ty as $crate::__internal::HasPinData>::__pin_data();
+ _ = data
+ .$field(ptr)
+ .init($crate::__internal::AlwaysFail::<$field_ty>::new());
};
};
@@ -902,7 +883,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,
@@ -922,15 +903,33 @@ macro_rules! assert_pinned {
#[cfg_attr(not(kernel), doc = "[`Box<T>`]: alloc::alloc::boxed::Box")]
#[must_use = "An initializer must be used in order to create its value."]
pub unsafe trait PinInit<T: ?Sized, E = Infallible>: Sized {
+ /// Alias of [`PinInit::__init`].
+ ///
+ /// New code should use `__init` instead.
+ ///
+ /// # Safety
+ ///
+ /// Same as `__init`.
+ #[inline(always)]
+ #[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) }
+ }
+
/// 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.
/// - 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<T, E>`, 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.
@@ -950,18 +949,47 @@ pub unsafe trait PinInit<T: ?Sized, E = Infallible>: Sized {
/// Ok(())
/// });
/// ```
+ #[inline]
fn pin_chain<F>(self, f: F) -> ChainPinInit<Self, F, T, E>
where
F: FnOnce(Pin<&mut T>) -> Result<(), E>,
{
- ChainPinInit(self, f, PhantomData)
+ ChainPinInit(self, f, __internal::PhantomInvariant::new())
}
}
+/// 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<T, E>`, this requirement is cancelled and it may be moved.
+#[inline(always)]
+pub unsafe fn raw_init<T>(slot: *mut T, init: impl PinInit<T>) {
+ // 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<T, E>`, this requirement is cancelled and it may be moved.
+#[inline(always)]
+pub unsafe fn raw_try_init<T, E>(slot: *mut T, init: impl PinInit<T, E>) -> Result<(), E> {
+ // SAFETY: Per safety requirement.
+ unsafe { init.__init(slot) }
+}
+
/// An initializer returned by [`PinInit::pin_chain`].
-pub struct ChainPinInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, T)>);
+pub struct ChainPinInit<I, F, T: ?Sized, E>(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.
@@ -970,15 +998,14 @@ where
I: PinInit<T, E>,
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 { 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) })
+ #[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) };
+ let mut guard = slot.init(self.0)?;
+ (self.1)(guard.let_binding())?;
+ core::mem::forget(guard);
+ Ok(())
}
}
@@ -995,19 +1022,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<T, E>`] 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,
@@ -1021,15 +1037,6 @@ where
#[cfg_attr(not(kernel), doc = "[`Box<T>`]: alloc::alloc::boxed::Box")]
#[must_use = "An initializer must be used in order to create its value."]
pub unsafe trait Init<T: ?Sized, E = Infallible>: PinInit<T, E> {
- /// 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.
///
@@ -1038,7 +1045,6 @@ pub unsafe trait Init<T: ?Sized, E = Infallible>: PinInit<T, E> {
/// # Examples
///
/// ```rust
- /// # #![expect(clippy::disallowed_names)]
/// use pin_init::{init, init_zeroed, Init};
///
/// struct Foo {
@@ -1058,44 +1064,68 @@ pub unsafe trait Init<T: ?Sized, E = Infallible>: PinInit<T, E> {
/// Ok(())
/// });
/// ```
+ #[inline]
fn chain<F>(self, f: F) -> ChainInit<Self, F, T, E>
where
F: FnOnce(&mut T) -> Result<(), E>,
{
- ChainInit(self, f, PhantomData)
+ ChainInit(self, f, __internal::PhantomInvariant::new())
}
}
/// An initializer returned by [`Init::chain`].
-pub struct ChainInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, T)>);
+pub struct ChainInit<I, F, T: ?Sized, E>(I, F, __internal::PhantomInvariant<(E, T)>);
+
+// SAFETY: The `__init` function does not rely on the pinning requirement.
+unsafe impl<T: ?Sized, E, I, F> Init<T, E> for ChainInit<I, F, T, E>
+where
+ I: Init<T, E>,
+ 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<T: ?Sized, E, I, F> Init<T, E> for ChainInit<I, F, T, E>
+unsafe impl<T: ?Sized, E, I, F> PinInit<T, E> for ChainInit<I, F, T, E>
where
I: Init<T, E>,
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`.
- 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(())
}
}
-// SAFETY: `__pinned_init` behaves exactly the same as `__init`.
-unsafe impl<T: ?Sized, E, I, F> PinInit<T, E> for ChainInit<I, F, T, E>
+/// 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 `__init` functions.
+struct InitClosure<F, T: ?Sized>(F, __internal::PhantomInvariant<T>);
+
+// 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<T: ?Sized, F, E> Init<T, E> for InitClosure<F, T> where
+ F: FnOnce(*mut T) -> Result<(), E>
+{
+}
+
+// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the
+// `__init` invariants.
+unsafe impl<T: ?Sized, F, E> PinInit<T, E> for InitClosure<F, T>
where
- I: Init<T, E>,
- F: FnOnce(&mut T) -> Result<(), E>,
+ 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) }
+ #[inline]
+ unsafe fn __init(self, slot: *mut T) -> Result<(), E> {
+ (self.0)(slot)
}
}
@@ -1115,7 +1145,7 @@ where
pub const unsafe fn pin_init_from_closure<T: ?Sized, E>(
f: impl FnOnce(*mut T) -> Result<(), E>,
) -> impl PinInit<T, E> {
- __internal::InitClosure(f, PhantomData)
+ InitClosure(f, __internal::PhantomInvariant::new())
}
/// Creates a new [`Init<T, E>`] from the given closure.
@@ -1134,7 +1164,7 @@ pub const unsafe fn pin_init_from_closure<T: ?Sized, E>(
pub const unsafe fn init_from_closure<T: ?Sized, E>(
f: impl FnOnce(*mut T) -> Result<(), E>,
) -> impl Init<T, E> {
- __internal::InitClosure(f, PhantomData)
+ InitClosure(f, __internal::PhantomInvariant::new())
}
/// Changes the to be initialized type.
@@ -1143,14 +1173,11 @@ pub const unsafe fn init_from_closure<T: ?Sized, E>(
///
/// - `*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<T, U, E>(init: impl PinInit<T, E>) -> impl PinInit<U, E> {
// SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety
// requirements.
- let res = unsafe { pin_init_from_closure(|ptr: *mut U| init.__pinned_init(ptr.cast::<T>())) };
- // FIXME: remove the let statement once the nightly-MSRV allows it (1.78 otherwise encounters a
- // cycle when computing the type returned by this function)
- #[allow(clippy::let_and_return)]
- res
+ unsafe { pin_init_from_closure(|ptr: *mut U| init.__init(ptr.cast::<T>())) }
}
/// Changes the to be initialized type.
@@ -1159,14 +1186,11 @@ pub const unsafe fn cast_pin_init<T, U, E>(init: impl PinInit<T, E>) -> 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<T, U, E>(init: impl Init<T, E>) -> impl Init<U, E> {
// SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety
// requirements.
- let res = unsafe { init_from_closure(|ptr: *mut U| init.__init(ptr.cast::<T>())) };
- // FIXME: remove the let statement once the nightly-MSRV allows it (1.78 otherwise encounters a
- // cycle when computing the type returned by this function)
- #[allow(clippy::let_and_return)]
- res
+ unsafe { init_from_closure(|ptr: *mut U| init.__init(ptr.cast::<T>())) }
}
/// An initializer that leaves the memory uninitialized.
@@ -1178,6 +1202,77 @@ pub fn uninit<T, E>() -> impl Init<MaybeUninit<T>, E> {
unsafe { init_from_closure(|_| Ok(())) }
}
+/// Array initializer from element initializer.
+struct ArrayInit<T: ?Sized, F>(F, __internal::PhantomInvariant<T>);
+
+// 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<T, F, I, E, const N: usize> PinInit<[T; N], E> for ArrayInit<T, F>
+where
+ F: FnMut(usize) -> I,
+ I: PinInit<T, E>,
+{
+ unsafe fn __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<T> {
+ /// A pointer to the first element of the array.
+ ptr: *mut T,
+ /// The number of initialized elements in the array.
+ num_init: usize,
+ }
+
+ impl<T> Drop for ArrayInitGuard<T> {
+ #[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::<T>(),
+ 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.__init(&raw mut (*slot)[i]) }?;
+ }
+
+ // Dismiss the drop guard now that all elements are initialized.
+ core::mem::forget(guard);
+ Ok(())
+ }
+}
+
+// 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<T, F, I, E, const N: usize> Init<[T; N], E> for ArrayInit<T, F>
+where
+ F: FnMut(usize) -> I,
+ I: Init<T, E>,
+{
+}
+
/// Initializes an array by initializing each element via the provided initializer.
///
/// # Examples
@@ -1188,32 +1283,14 @@ pub fn uninit<T, E>() -> impl Init<MaybeUninit<T>, E> {
/// 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<I, const N: usize, T, E>(
- mut make_init: impl FnMut(usize) -> I,
+ make_init: impl FnMut(usize) -> I,
) -> impl Init<[T; N], E>
where
I: Init<T, E>,
{
- let init = move |slot: *mut [T; N]| {
- let slot = slot.cast::<T>();
- 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.
@@ -1231,32 +1308,14 @@ 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<I, const N: usize, T, E>(
- mut make_init: impl FnMut(usize) -> I,
+ make_init: impl FnMut(usize) -> I,
) -> impl PinInit<[T; N], E>
where
I: PinInit<T, E>,
{
- let init = move |slot: *mut [T; N]| {
- let slot = slot.cast::<T>();
- 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.
@@ -1285,6 +1344,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<T, E, F, I>(make_init: F) -> impl PinInit<T, E>
where
F: FnOnce() -> Result<I, E>,
@@ -1292,13 +1352,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)
})
}
}
@@ -1328,6 +1388,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<T, E, F, I>(make_init: F) -> impl Init<T, E>
where
F: FnOnce() -> Result<I, E>,
@@ -1346,41 +1407,29 @@ where
}
}
-// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of `slot`.
-unsafe impl<T> Init<T> 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<T> Init<T> 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<T> PinInit<T> for T {
- unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), Infallible> {
+ #[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) };
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<T, E> Init<T, E> for Result<T, 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(())
- }
-}
+// SAFETY: The `__init` function does not rely on slot being pinned after it returns.
+unsafe impl<T, E> Init<T, E> for Result<T, E> {}
-// 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<T, E> PinInit<T, E> for Result<T, E> {
- unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
+ #[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?) };
Ok(())
@@ -1406,6 +1455,7 @@ pub trait InPlaceWrite<T> {
impl<T> InPlaceWrite<T> for &'static mut MaybeUninit<T> {
type Initialized = &'static mut T;
+ #[inline]
fn write_init<E>(self, init: impl Init<T, E>) -> Result<Self::Initialized, E> {
let slot = self.as_mut_ptr();
@@ -1416,6 +1466,7 @@ impl<T> InPlaceWrite<T> for &'static mut MaybeUninit<T> {
unsafe { Ok(self.assume_init_mut()) }
}
+ #[inline]
fn write_pin_init<E>(self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, E> {
let slot = self.as_mut_ptr();
@@ -1423,7 +1474,7 @@ impl<T> InPlaceWrite<T> for &'static mut MaybeUninit<T> {
//
// 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() }))
@@ -1495,10 +1546,13 @@ pub unsafe trait Zeroable {
/// Whenever a type implements [`Zeroable`], this function should be preferred over
/// [`core::mem::zeroed()`] or using `MaybeUninit<T>::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 {
@@ -1506,10 +1560,11 @@ 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);
/// ```
+ #[inline]
fn zeroed() -> Self
where
Self: Sized,
@@ -1518,27 +1573,6 @@ pub unsafe trait Zeroable {
}
}
-/// Marker trait for types that allow `Option<Self>` to be set to all zeroes in order to write
-/// `None` to that location.
-///
-/// # Safety
-///
-/// The implementer needs to ensure that `unsafe impl Zeroable for Option<Self> {}` is sound.
-pub unsafe trait ZeroableOption {}
-
-// SAFETY: by the safety requirement of `ZeroableOption`, this is valid.
-unsafe impl<T: ZeroableOption> Zeroable for Option<T> {}
-
-// SAFETY: `Option<&T>` is part of the option layout optimization guarantee:
-// <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
-unsafe impl<T> ZeroableOption for &T {}
-// SAFETY: `Option<&mut T>` is part of the option layout optimization guarantee:
-// <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
-unsafe impl<T> ZeroableOption for &mut T {}
-// SAFETY: `Option<NonNull<T>>` is part of the option layout optimization guarantee:
-// <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
-unsafe impl<T> ZeroableOption for NonNull<T> {}
-
/// Create an initializer for a zeroed `T`.
///
/// The returned initializer will write `0x00` to every byte of the given `slot`.
@@ -1559,6 +1593,9 @@ pub fn init_zeroed<T: Zeroable>() -> impl Init<T> {
/// Whenever a type implements [`Zeroable`], this function should be preferred over
/// [`core::mem::zeroed()`] or using `MaybeUninit<T>::zeroed().assume_init()`.
///
+/// While const traits remain unstable, this function serves as the `const` version of
+/// [`Zeroable::zeroed()`].
+///
/// # Examples
///
/// ```
@@ -1574,6 +1611,7 @@ pub fn init_zeroed<T: Zeroable>() -> impl Init<T> {
/// assert_eq!(point.x, 0);
/// assert_eq!(point.y, 0);
/// ```
+#[inline]
pub const fn zeroed<T: Zeroable>() -> T {
// SAFETY:By the type invariants of `Zeroable`, all zeroes is a valid bit pattern for `T`.
unsafe { core::mem::zeroed() }
@@ -1610,13 +1648,6 @@ impl_zeroable! {
// SAFETY: `T: Zeroable` and `UnsafeCell` is `repr(transparent)`.
{<T: ?Sized + Zeroable>} UnsafeCell<T>,
- // SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee:
- // <https://doc.rust-lang.org/stable/std/option/index.html#representation>).
- Option<NonZeroU8>, Option<NonZeroU16>, Option<NonZeroU32>, Option<NonZeroU64>,
- Option<NonZeroU128>, Option<NonZeroUsize>,
- Option<NonZeroI8>, Option<NonZeroI16>, Option<NonZeroI32>, Option<NonZeroI64>,
- Option<NonZeroI128>, Option<NonZeroIsize>,
-
// SAFETY: `null` pointer is valid.
//
// We cannot use `T: ?Sized`, since the VTABLE pointer part of fat pointers is not allowed to be
@@ -1635,8 +1666,14 @@ impl_zeroable! {
}
macro_rules! impl_tuple_zeroable {
- ($(,)?) => {};
+ ($first:ident, $(,)?) => {
+ #[cfg_attr(all(USE_RUSTC_FEATURES, doc), doc(fake_variadic))]
+ /// Implemented for tuples up to 10 items long.
+ // SAFETY: All elements are zeroable and padding can be zero.
+ unsafe impl<$first: Zeroable> Zeroable for ($first,) {}
+ };
($first:ident, $($t:ident),* $(,)?) => {
+ #[cfg_attr(doc, doc(hidden))]
// SAFETY: All elements are zeroable and padding can be zero.
unsafe impl<$first: Zeroable, $($t: Zeroable),*> Zeroable for ($first, $($t),*) {}
impl_tuple_zeroable!($($t),* ,);
@@ -1645,13 +1682,33 @@ macro_rules! impl_tuple_zeroable {
impl_tuple_zeroable!(A, B, C, D, E, F, G, H, I, J);
+/// Marker trait for types that allow `Option<Self>` to be set to all zeroes in order to write
+/// `None` to that location.
+///
+/// # Safety
+///
+/// The implementer needs to ensure that `unsafe impl Zeroable for Option<Self> {}` is sound.
+pub unsafe trait ZeroableOption {}
+
+// SAFETY: by the safety requirement of `ZeroableOption`, this is valid.
+unsafe impl<T: ZeroableOption> Zeroable for Option<T> {}
+
macro_rules! impl_fn_zeroable_option {
([$($abi:literal),* $(,)?] $args:tt) => {
$(impl_fn_zeroable_option!({extern $abi} $args);)*
$(impl_fn_zeroable_option!({unsafe extern $abi} $args);)*
};
({$($prefix:tt)*} {$(,)?}) => {};
+ ({$($prefix:tt)*} {$ret:ident, $arg:ident $(,)?}) => {
+ #[cfg_attr(all(USE_RUSTC_FEATURES, doc), doc(fake_variadic))]
+ /// Implemented for function pointers with up to 20 arity.
+ // SAFETY: function pointers are part of the option layout optimization:
+ // <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
+ unsafe impl<$ret, $arg> ZeroableOption for $($prefix)* fn($arg) -> $ret {}
+ impl_fn_zeroable_option!({$($prefix)*} {$arg,});
+ };
({$($prefix:tt)*} {$ret:ident, $($rest:ident),* $(,)?}) => {
+ #[cfg_attr(doc, doc(hidden))]
// SAFETY: function pointers are part of the option layout optimization:
// <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
unsafe impl<$ret, $($rest),*> ZeroableOption for $($prefix)* fn($($rest),*) -> $ret {}
@@ -1661,6 +1718,29 @@ macro_rules! impl_fn_zeroable_option {
impl_fn_zeroable_option!(["Rust", "C"] { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U });
+macro_rules! impl_zeroable_option {
+ ($($({$($generics:tt)*})? $t:ty, )*) => {
+ // SAFETY: Safety comments written in the macro invocation.
+ $(unsafe impl$($($generics)*)? ZeroableOption for $t {})*
+ };
+}
+
+impl_zeroable_option! {
+ // SAFETY: `Option<&T>` is part of the option layout optimization guarantee:
+ // <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
+ {<T: ?Sized>} &T,
+ // SAFETY: `Option<&mut T>` is part of the option layout optimization guarantee:
+ // <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
+ {<T: ?Sized>} &mut T,
+ // SAFETY: `Option<NonNull<T>>` is part of the option layout optimization guarantee:
+ // <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
+ {<T: ?Sized>} NonNull<T>,
+ // SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee:
+ // <https://doc.rust-lang.org/stable/std/option/index.html#representation>).
+ NonZero<u8>, NonZero<u16>, NonZero<u32>, NonZero<u64>, NonZero<u128>, NonZero<usize>,
+ NonZero<i8>, NonZero<i16>, NonZero<i32>, NonZero<i64>, NonZero<i128>, NonZero<isize>,
+}
+
/// This trait allows creating an instance of `Self` which contains exactly one
/// [structurally pinned value](https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning).
///
@@ -1692,6 +1772,7 @@ pub trait Wrapper<T> {
}
impl<T> Wrapper<T> for UnsafeCell<T> {
+ #[inline]
fn pin_init<E>(value_init: impl PinInit<T, E>) -> impl PinInit<Self, E> {
// SAFETY: `UnsafeCell<T>` has a compatible layout to `T`.
unsafe { cast_pin_init(value_init) }
@@ -1699,6 +1780,7 @@ impl<T> Wrapper<T> for UnsafeCell<T> {
}
impl<T> Wrapper<T> for MaybeUninit<T> {
+ #[inline]
fn pin_init<E>(value_init: impl PinInit<T, E>) -> impl PinInit<Self, E> {
// SAFETY: `MaybeUninit<T>` has a compatible layout to `T`.
unsafe { cast_pin_init(value_init) }
@@ -1707,6 +1789,7 @@ impl<T> Wrapper<T> for MaybeUninit<T> {
#[cfg(all(feature = "unsafe-pinned", CONFIG_RUSTC_HAS_UNSAFE_PINNED))]
impl<T> Wrapper<T> for core::pin::UnsafePinned<T> {
+ #[inline]
fn pin_init<E>(init: impl PinInit<T, E>) -> impl PinInit<Self, E> {
// SAFETY: `UnsafePinned<T>` has a compatible layout to `T`.
unsafe { cast_pin_init(init) }