pin-init changes for v7.2

User visible changes:
 
 - `non_snake_case` warnings will no longer be generated for identifiers that
   are syntactically just users of a field name. This would allow all
   `#[allow(non_snake_case)]` in nova-core to be removed, which I will send to
   nova tree next cycle.
 
 - Non-cfg attributes are now properly filtered out in derived structs. This
   improves pin-init compatibility with other derive macros.
 
 - Projection types have their where clause properly inserted now.
 
 Other changes:
 
 - MSRV is bumped to 1.82 and associated cleanups.
 
 - There is an overhaul on how init slots are projected. The new approach is
   easier to justify with safety comments.
 
 - More functions are marked as inline, which should help mitigate super-long
   symbol name issue due to lack of inlining.
 
 - Various small code quality cleanups.
 -----BEGIN PGP SIGNATURE-----
 
 iIcEABYKAC8WIQRIiwFmsgGbHdMqd+kPoQF7f9ATtgUCah6+GREcZ2FyeUBnYXJ5
 Z3VvLm5ldAAKCRAPoQF7f9ATttL5AP0YPT6NwXTHG9XJU6s/dGYsRO+8c1W5Q0oJ
 jo3OYOqaMQD+K9uZMradLq60+5AHhRiJI/wIbhZmW42Y/fIRvXrbsgc=
 =um+N
 -----END PGP SIGNATURE-----

Merge tag 'pin-init-v7.2' of https://github.com/Rust-for-Linux/linux into rust-next

Pull pin-init updates from Gary Guo:
 "User visible changes:

   - Do not generate 'non_snake_case' warnings for identifiers that are
     syntactically just users of a field name. This would allow all
     '#[allow(non_snake_case)]' in nova-core to be removed, which I will
     send to the nova tree next cycle.

   - Filter non-cfg attributes out properly in derived structs. This
     improves pin-init compatibility with other derive macros.

   - Insert projection types' where clause properly.

  Other changes:

   - Bump MSRV to 1.82, plus associated cleanups.

   - Overhaul how init slots are projected. The new approach is easier
     to justify with safety comments.

   - Mark more functions as inline, which should help mitigate the
     super-long symbol name issue due to lack of inlining.

   - Various small code quality cleanups."

* tag 'pin-init-v7.2' of https://github.com/Rust-for-Linux/linux: (27 commits)
  rust: pin_init: internal: use `loop {}` to produce never value
  rust: pin-init: remove `E` from `InitClosure`
  rust: pin-init: move `InitClosure` out from `__internal`
  rust: pin-init: docs: fix typos in MaybeZeroable documentation
  rust: pin-init: internal: suppress `non_snake_case` lint in `[pin_]init!`
  rust: pin-init: internal: suppress `non_snake_case` lint in `#[pin_data]`
  rust: pin-init: internal: pin_data: filter non-`#[cfg]` attr in generated code
  rust: pin-init: internal: project using full slot
  rust: pin-init: internal: project slots instead of references
  rust: pin-init: internal: make `make_closure` inherent methods
  rust: pin-init: internal: use marker on drop guard type for pinned fields
  rust: pin-init: internal: init: handle code blocks early
  rust: pin-init: internal: add `PhantomInvariant` and `PhantomInvariantLifetime`
  rust: pin-init: internal: pin_data: add struct to record field info
  rust: pin-init: internal: pin_data: use closure for `handle_field`
  rust: pin-init: examples: fix `useless_borrows_in_formatting` clippy warning
  rust: pin-init: internal: remove `collect_tuple` polyfill after MSRV bump
  rust: pin-init: internal: turn `PhantomPinned` error into warnings
  rust: pin-init: cleanup workaround for old Rust compiler
  rust: pin-init: fix badge URL in README
  ...
This commit is contained in:
Miguel Ojeda 2026-06-06 14:01:29 +02:00
commit 838a0871cb
14 changed files with 444 additions and 409 deletions

View File

@ -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]

View File

@ -1,8 +1,5 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))]
#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))]
use pin_init::*;
// Struct with size over 1GiB

View File

@ -11,6 +11,7 @@
pub struct Error;
impl From<Infallible> for Error {
#[inline]
fn from(e: Infallible) -> Self {
match e {}
}
@ -18,6 +19,7 @@ fn from(e: Infallible) -> Self {
#[cfg(feature = "alloc")]
impl From<AllocError> for Error {
#[inline]
fn from(_: AllocError) -> Self {
Self
}

View File

@ -2,8 +2,6 @@
#![allow(clippy::undocumented_unsafe_blocks)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))]
#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))]
use core::{
cell::Cell,

View File

@ -2,8 +2,6 @@
#![allow(clippy::undocumented_unsafe_blocks)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))]
#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))]
#![allow(clippy::missing_safety_doc)]
use core::{
@ -220,7 +218,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);
}
}

View File

@ -3,8 +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(USE_RUSTC_FEATURES, feature(lint_reasons))]
#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))]
#[cfg(not(windows))]
mod pthread_mtx {
@ -179,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);
}
}

View File

@ -2,8 +2,6 @@
#![allow(clippy::undocumented_unsafe_blocks)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))]
#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))]
#![allow(unused_imports)]
use core::{
@ -119,7 +117,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);
}
}

View File

@ -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 @@ pub(crate) fn error(&mut self, span: impl Spanned, msg: impl Display) -> ErrorGu
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 {

View File

@ -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 @@ fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
#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,14 +169,7 @@ fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
init(slot).map(|__InitOk| ())
};
// SAFETY: TODO
let init = unsafe { ::pin_init::#init_from_closure::<_, #error>(init) };
// FIXME: this let binding is required to avoid a compiler error (cycle when computing the
// opaque type returned by this function) before Rust 1.81. Remove after MSRV bump.
#[allow(
clippy::let_and_return,
reason = "some clippy versions warn about the let binding"
)]
init
unsafe { ::pin_init::#init_from_closure::<_, #error>(init) }
}})
}
@ -238,107 +228,82 @@ fn init_fields(
cfgs.retain(|attr| attr.path().is_ident("cfg"));
cfgs
};
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;)
let ident = match kind {
InitializerKind::Value { ident, .. } => ident,
InitializerKind::Init { ident, .. } => ident,
InitializerKind::Code { block, .. } => {
res.extend(quote! {
#(#attrs)*
#[allow(unused_braces)]
#block
});
// 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) };
}
}
continue;
}
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 { block: value, .. } => quote! {
#(#attrs)*
#[allow(unused_braces)]
#value
},
};
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()
}
};
res.extend(quote! {
#(#cfgs)*
// Create the drop guard.
//
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.
// - `(*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
// - `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
)
};
})
}
};
#(#cfgs)*
#[allow(unused_variables)]
let #ident = #accessor;
});
guards.push(guard);
guard_attrs.push(cfgs);
}
// `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)*
// 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
@ -389,7 +354,7 @@ fn make_field_check(
::core::ptr::write(slot, #path {
#(
#(#field_attrs)*
#field_name: ::core::panic!(),
#field_name: loop {},
)*
#zeroing_trailer
})

View File

@ -6,7 +6,6 @@
//! `pin-init` proc macros.
#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))]
// Documentation is done in the pin-init crate instead.
#![allow(missing_docs)]

View File

@ -7,7 +7,7 @@
parse_quote, parse_quote_spanned,
spanned::Spanned,
visit_mut::VisitMut,
Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause,
Attribute, Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause,
};
use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
@ -35,6 +35,12 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
}
}
struct FieldInfo<'a> {
field: &'a Field,
pinned: bool,
cfg_attrs: Vec<&'a Attribute>,
}
pub(crate) fn pin_data(
args: Args,
input: Item,
@ -73,24 +79,37 @@ 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 = len != field.attrs.len();
let cfg_attrs = field
.attrs
.iter()
.filter(|a| a.path().is_ident("cfg"))
.collect();
FieldInfo {
field: &*field,
pinned,
cfg_attrs,
}
})
.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 +162,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 +179,28 @@ 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;
let cfg_attrs = &f.cfg_attrs;
quote!(
#(#cfg_attrs)*
#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),*
}
@ -238,7 +266,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 +275,23 @@ 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 cfg_attrs = &field.cfg_attrs;
let ident = ident
.as_ref()
.expect("only structs with named fields are supported");
if *pinned {
if field.pinned {
(
quote!(
#(#attrs)*
#(#cfg_attrs)*
#vis #ident: ::core::pin::Pin<&'__pin mut #ty>,
),
quote!(
#(#no_doc_attrs)*
#(#cfg_attrs)*
// SAFETY: this field is structurally pinned.
#ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },
),
@ -280,31 +299,35 @@ fn generate_projections(
} else {
(
quote!(
#(#attrs)*
#(#cfg_attrs)*
#vis #ident: &'__pin mut #ty,
),
quote!(
#(#no_doc_attrs)*
#(#cfg_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 +359,54 @@ impl #impl_generics #ident #ty_generics
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 cfg_attrs = &f.cfg_attrs;
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.
#(#cfg_attrs)*
// 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,9 +415,7 @@ fn handle_field(
#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
@ -449,27 +433,30 @@ impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics
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;
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 +487,3 @@ fn visit_item_mut(&mut self, _: &mut Item) {
// 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)
}

View File

@ -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;

View File

@ -7,42 +7,54 @@
use super::*;
/// 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.
///
/// See the [nomicon] for what subtyping is. See also [this table].
///
/// 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.
///
/// [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 @@ pub unsafe fn new() -> Self {
///
/// 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,31 +96,13 @@ fn make_closure<F, E>(self, f: F) -> F
///
/// 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> {
fn clone(&self) -> Self {
@ -136,9 +112,15 @@ fn clone(&self) -> Self {
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.
@ -146,7 +128,7 @@ unsafe impl<T: ?Sized> HasInitData for T {
type InitData = AllData<T>;
unsafe fn __init_data() -> Self::InitData {
AllData(PhantomData)
AllData(PhantomInvariant::new())
}
}
@ -235,6 +217,87 @@ struct Foo {
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.__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.
@ -243,11 +306,13 @@ struct Foo {
///
/// - `ptr` is valid and properly aligned.
/// - `*ptr` is initialized and owned by this guard.
pub struct DropGuard<T: ?Sized> {
/// - if `P` is `Pinned`, `ptr` is pinned.
pub struct DropGuard<P, T: ?Sized> {
ptr: *mut T,
phantom: PhantomData<P>,
}
impl<T: ?Sized> DropGuard<T> {
impl<P, T: ?Sized> DropGuard<P, T> {
/// 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`].
@ -256,12 +321,18 @@ impl<T: ?Sized> DropGuard<T> {
///
/// - `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<T: ?Sized> DropGuard<Unpinned, T> {
/// Create a let binding for accessor use.
#[inline]
pub fn let_binding(&mut self) -> &mut T {
@ -270,7 +341,17 @@ pub fn let_binding(&mut self) -> &mut T {
}
}
impl<T: ?Sized> Drop for DropGuard<T> {
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: `self.ptr` is valid, properly aligned and `*self.ptr` is owned by this guard.

View File

@ -263,12 +263,6 @@
//! [`impl Init<T, E>`]: crate::Init
//! [Rust-for-Linux]: https://rust-for-linux.com/
#![cfg_attr(USE_RUSTC_FEATURES, feature(lint_reasons))]
#![cfg_attr(USE_RUSTC_FEATURES, feature(raw_ref_op))]
#![cfg_attr(
all(any(feature = "alloc", feature = "std"), USE_RUSTC_FEATURES),
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))]
@ -437,7 +431,7 @@
/// ```
/// use pin_init::MaybeZeroable;
///
/// // implmements `Zeroable`
/// // implements `Zeroable`
/// #[derive(MaybeZeroable)]
/// pub struct DriverData {
/// pub(crate) id: i64,
@ -445,7 +439,7 @@
/// len: usize,
/// }
///
/// // does not implmement `Zeroable`
/// // does not implement `Zeroable`
/// #[derive(MaybeZeroable)]
/// pub struct DriverData2 {
/// pub(crate) id: i64,
@ -873,12 +867,12 @@ macro_rules! stack_try_pin_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());
};
};
@ -953,12 +947,12 @@ 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())
}
}
/// 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
// - returns `Ok(())` on successful initialization,
@ -1061,12 +1055,12 @@ 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 is implemented such that it
// - returns `Ok(())` on successful initialization,
@ -1098,6 +1092,36 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), 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 `__pinned_init`/`__init` functions.
struct InitClosure<F, T: ?Sized>(F, __internal::PhantomInvariant<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>
where
F: FnOnce(*mut T) -> Result<(), E>,
{
#[inline]
unsafe fn __init(self, slot: *mut T) -> Result<(), E> {
(self.0)(slot)
}
}
// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the
// `__pinned_init` invariants.
unsafe impl<T: ?Sized, F, E> PinInit<T, E> for InitClosure<F, T>
where
F: FnOnce(*mut T) -> Result<(), E>,
{
#[inline]
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
(self.0)(slot)
}
}
/// Creates a new [`PinInit<T, E>`] from the given closure.
///
/// # Safety
@ -1114,7 +1138,7 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
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.
@ -1133,7 +1157,7 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), 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.
@ -1145,14 +1169,7 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
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: this let binding is required to avoid a compiler error (cycle when computing the opaque
// type returned by this function) before Rust 1.81. Remove after MSRV bump.
#[allow(
clippy::let_and_return,
reason = "some clippy versions warn about the let binding"
)]
res
unsafe { pin_init_from_closure(|ptr: *mut U| init.__pinned_init(ptr.cast::<T>())) }
}
/// Changes the to be initialized type.
@ -1164,14 +1181,7 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
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: this let binding is required to avoid a compiler error (cycle when computing the opaque
// type returned by this function) before Rust 1.81. Remove after MSRV bump.
#[allow(
clippy::let_and_return,
reason = "some clippy versions warn about the let binding"
)]
res
unsafe { init_from_closure(|ptr: *mut U| init.__init(ptr.cast::<T>())) }
}
/// An initializer that leaves the memory uninitialized.
@ -1523,27 +1533,6 @@ fn zeroed() -> Self
}
}
/// 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`.
@ -1649,6 +1638,17 @@ unsafe impl<$first: Zeroable, $($t: Zeroable),*> Zeroable for ($first, $($t),*)
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);)*
@ -1674,18 +1674,27 @@ unsafe impl<$ret, $($rest),*> ZeroableOption for $($prefix)* fn($($rest),*) -> $
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_non_zero_int_zeroable_option {
($($int:ty),* $(,)?) => {
// SAFETY: Safety comment written in the macro invocation.
$(unsafe impl ZeroableOption for $int {})*
macro_rules! impl_zeroable_option {
($($({$($generics:tt)*})? $t:ty, )*) => {
// SAFETY: Safety comments written in the macro invocation.
$(unsafe impl$($($generics)*)? ZeroableOption for $t {})*
};
}
impl_non_zero_int_zeroable_option! {
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>).
NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize,
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