summaryrefslogtreecommitdiff
path: root/rust/pin-init
diff options
context:
space:
mode:
Diffstat (limited to 'rust/pin-init')
-rw-r--r--rust/pin-init/README.md16
-rw-r--r--rust/pin-init/examples/big_struct_in_place.rs28
-rw-r--r--rust/pin-init/examples/linked_list.rs11
-rw-r--r--rust/pin-init/examples/mutex.rs98
-rw-r--r--rust/pin-init/examples/pthread_mutex.rs10
-rw-r--r--rust/pin-init/examples/static_init.rs76
-rw-r--r--rust/pin-init/internal/src/lib.rs6
-rw-r--r--rust/pin-init/internal/src/pinned_drop.rs3
-rw-r--r--rust/pin-init/internal/src/zeroable.rs27
-rw-r--r--rust/pin-init/src/__internal.rs1
-rw-r--r--rust/pin-init/src/alloc.rs8
-rw-r--r--rust/pin-init/src/lib.rs306
-rw-r--r--rust/pin-init/src/macros.rs107
13 files changed, 554 insertions, 143 deletions
diff --git a/rust/pin-init/README.md b/rust/pin-init/README.md
index 3d04796b212b..a4c01a8d78b2 100644
--- a/rust/pin-init/README.md
+++ b/rust/pin-init/README.md
@@ -40,6 +40,12 @@ However, using the crate on stable compilers is possible by disabling `alloc`. I
will require the `std` feature, because stable compilers have neither `Box` nor `Arc` in no-std
mode.
+### Nightly needed for `unsafe-pinned` feature
+
+This feature enables the `Wrapper` implementation on the unstable `core::pin::UnsafePinned` type.
+This requires the [`unsafe_pinned` unstable feature](https://github.com/rust-lang/rust/issues/125735)
+and therefore a nightly compiler. Note that this feature is not enabled by default.
+
## Overview
To initialize a `struct` with an in-place constructor you will need two things:
@@ -119,7 +125,7 @@ impl DriverData {
fn new() -> impl PinInit<Self, Error> {
try_pin_init!(Self {
status <- CMutex::new(0),
- buffer: Box::init(pin_init::zeroed())?,
+ buffer: Box::init(pin_init::init_zeroed())?,
}? Error)
}
}
@@ -216,13 +222,15 @@ the `kernel` crate. The [`sync`] module is a good starting point.
[`sync`]: https://rust.docs.kernel.org/kernel/sync/index.html
[pinning]: https://doc.rust-lang.org/std/pin/index.html
-[structurally pinned fields]: https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field
+[structurally pinned fields]: https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning
[stack]: https://docs.rs/pin-init/latest/pin_init/macro.stack_pin_init.html
-[`Arc<T>`]: https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html
-[`Box<T>`]: https://doc.rust-lang.org/stable/alloc/boxed/struct.Box.html
[`impl PinInit<Foo>`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html
[`impl PinInit<T, E>`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html
[`impl Init<T, E>`]: https://docs.rs/pin-init/latest/pin_init/trait.Init.html
[Rust-for-Linux]: https://rust-for-linux.com/
<!-- cargo-rdme end -->
+
+<!-- These links are not picked up by cargo-rdme, since they are behind cfgs... -->
+[`Arc<T>`]: https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html
+[`Box<T>`]: https://doc.rust-lang.org/stable/alloc/boxed/struct.Box.html
diff --git a/rust/pin-init/examples/big_struct_in_place.rs b/rust/pin-init/examples/big_struct_in_place.rs
index 30d44a334ffd..c05139927486 100644
--- a/rust/pin-init/examples/big_struct_in_place.rs
+++ b/rust/pin-init/examples/big_struct_in_place.rs
@@ -4,6 +4,7 @@ use pin_init::*;
// Struct with size over 1GiB
#[derive(Debug)]
+#[allow(dead_code)]
pub struct BigStruct {
buf: [u8; 1024 * 1024 * 1024],
a: u64,
@@ -20,20 +21,23 @@ pub struct ManagedBuf {
impl ManagedBuf {
pub fn new() -> impl Init<Self> {
- init!(ManagedBuf { buf <- zeroed() })
+ init!(ManagedBuf { buf <- init_zeroed() })
}
}
fn main() {
- // we want to initialize the struct in-place, otherwise we would get a stackoverflow
- let buf: Box<BigStruct> = Box::init(init!(BigStruct {
- buf <- zeroed(),
- a: 7,
- b: 186,
- c: 7789,
- d: 34,
- managed_buf <- ManagedBuf::new(),
- }))
- .unwrap();
- println!("{}", core::mem::size_of_val(&*buf));
+ #[cfg(any(feature = "std", feature = "alloc"))]
+ {
+ // we want to initialize the struct in-place, otherwise we would get a stackoverflow
+ let buf: Box<BigStruct> = Box::init(init!(BigStruct {
+ buf <- init_zeroed(),
+ a: 7,
+ b: 186,
+ c: 7789,
+ d: 34,
+ managed_buf <- ManagedBuf::new(),
+ }))
+ .unwrap();
+ println!("{}", core::mem::size_of_val(&*buf));
+ }
}
diff --git a/rust/pin-init/examples/linked_list.rs b/rust/pin-init/examples/linked_list.rs
index 6d7eb0a0ec0d..f9e117c7dfe0 100644
--- a/rust/pin-init/examples/linked_list.rs
+++ b/rust/pin-init/examples/linked_list.rs
@@ -2,6 +2,7 @@
#![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,
@@ -13,8 +14,9 @@ use core::{
use pin_init::*;
-#[expect(unused_attributes)]
+#[allow(unused_attributes)]
mod error;
+#[allow(unused_imports)]
use error::Error;
#[pin_data(PinnedDrop)]
@@ -38,6 +40,7 @@ impl ListHead {
}
#[inline]
+ #[allow(dead_code)]
pub fn insert_next(list: &ListHead) -> impl PinInit<Self, Infallible> + '_ {
try_pin_init!(&this in Self {
prev: list.next.prev().replace(unsafe { Link::new_unchecked(this)}),
@@ -111,6 +114,7 @@ impl Link {
}
#[inline]
+ #[allow(dead_code)]
fn prev(&self) -> &Link {
unsafe { &(*self.0.get().as_ptr()).prev }
}
@@ -137,7 +141,12 @@ impl Link {
}
#[allow(dead_code)]
+#[cfg(not(any(feature = "std", feature = "alloc")))]
+fn main() {}
+
+#[allow(dead_code)]
#[cfg_attr(test, test)]
+#[cfg(any(feature = "std", feature = "alloc"))]
fn main() -> Result<(), Error> {
let a = Box::pin_init(ListHead::new())?;
stack_pin_init!(let b = ListHead::insert_next(&a));
diff --git a/rust/pin-init/examples/mutex.rs b/rust/pin-init/examples/mutex.rs
index 073bb79341d1..9f295226cd64 100644
--- a/rust/pin-init/examples/mutex.rs
+++ b/rust/pin-init/examples/mutex.rs
@@ -2,6 +2,7 @@
#![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::{
@@ -11,14 +12,15 @@ use core::{
pin::Pin,
sync::atomic::{AtomicBool, Ordering},
};
+#[cfg(feature = "std")]
use std::{
sync::Arc,
- thread::{self, park, sleep, Builder, Thread},
+ thread::{self, sleep, Builder, Thread},
time::Duration,
};
use pin_init::*;
-#[expect(unused_attributes)]
+#[allow(unused_attributes)]
#[path = "./linked_list.rs"]
pub mod linked_list;
use linked_list::*;
@@ -35,6 +37,7 @@ impl SpinLock {
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
+ #[cfg(feature = "std")]
while self.inner.load(Ordering::Relaxed) {
thread::yield_now();
}
@@ -93,7 +96,8 @@ impl<T> CMutex<T> {
// println!("wait list length: {}", self.wait_list.size());
while self.locked.get() {
drop(sguard);
- park();
+ #[cfg(feature = "std")]
+ thread::park();
sguard = self.spin_lock.acquire();
}
// This does have an effect, as the ListHead inside wait_entry implements Drop!
@@ -130,8 +134,11 @@ impl<T> Drop for CMutexGuard<'_, T> {
let sguard = self.mtx.spin_lock.acquire();
self.mtx.locked.set(false);
if let Some(list_field) = self.mtx.wait_list.next() {
- let wait_entry = list_field.as_ptr().cast::<WaitEntry>();
- unsafe { (*wait_entry).thread.unpark() };
+ let _wait_entry = list_field.as_ptr().cast::<WaitEntry>();
+ #[cfg(feature = "std")]
+ unsafe {
+ (*_wait_entry).thread.unpark()
+ };
}
drop(sguard);
}
@@ -158,52 +165,61 @@ impl<T> DerefMut for CMutexGuard<'_, T> {
struct WaitEntry {
#[pin]
wait_list: ListHead,
+ #[cfg(feature = "std")]
thread: Thread,
}
impl WaitEntry {
#[inline]
fn insert_new(list: &ListHead) -> impl PinInit<Self> + '_ {
- pin_init!(Self {
- thread: thread::current(),
- wait_list <- ListHead::insert_prev(list),
- })
+ #[cfg(feature = "std")]
+ {
+ pin_init!(Self {
+ thread: thread::current(),
+ wait_list <- ListHead::insert_prev(list),
+ })
+ }
+ #[cfg(not(feature = "std"))]
+ {
+ pin_init!(Self {
+ wait_list <- ListHead::insert_prev(list),
+ })
+ }
}
}
-#[cfg(not(any(feature = "std", feature = "alloc")))]
-fn main() {}
-
-#[allow(dead_code)]
#[cfg_attr(test, test)]
-#[cfg(any(feature = "std", feature = "alloc"))]
+#[allow(dead_code)]
fn main() {
- let mtx: Pin<Arc<CMutex<usize>>> = Arc::pin_init(CMutex::new(0)).unwrap();
- let mut handles = vec![];
- let thread_count = 20;
- let workload = if cfg!(miri) { 100 } else { 1_000 };
- for i in 0..thread_count {
- let mtx = mtx.clone();
- handles.push(
- Builder::new()
- .name(format!("worker #{i}"))
- .spawn(move || {
- for _ in 0..workload {
- *mtx.lock() += 1;
- }
- println!("{i} halfway");
- sleep(Duration::from_millis((i as u64) * 10));
- for _ in 0..workload {
- *mtx.lock() += 1;
- }
- println!("{i} finished");
- })
- .expect("should not fail"),
- );
- }
- for h in handles {
- h.join().expect("thread panicked");
+ #[cfg(feature = "std")]
+ {
+ let mtx: Pin<Arc<CMutex<usize>>> = Arc::pin_init(CMutex::new(0)).unwrap();
+ let mut handles = vec![];
+ let thread_count = 20;
+ let workload = if cfg!(miri) { 100 } else { 1_000 };
+ for i in 0..thread_count {
+ let mtx = mtx.clone();
+ handles.push(
+ Builder::new()
+ .name(format!("worker #{i}"))
+ .spawn(move || {
+ for _ in 0..workload {
+ *mtx.lock() += 1;
+ }
+ println!("{i} halfway");
+ sleep(Duration::from_millis((i as u64) * 10));
+ for _ in 0..workload {
+ *mtx.lock() += 1;
+ }
+ println!("{i} finished");
+ })
+ .expect("should not fail"),
+ );
+ }
+ for h in handles {
+ h.join().expect("thread panicked");
+ }
+ println!("{:?}", &*mtx.lock());
+ assert_eq!(*mtx.lock(), workload * thread_count * 2);
}
- 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 9164298c44c0..49b004c8c137 100644
--- a/rust/pin-init/examples/pthread_mutex.rs
+++ b/rust/pin-init/examples/pthread_mutex.rs
@@ -1,8 +1,10 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
-// inspired by https://github.com/nbdd0121/pin-init/blob/trunk/examples/pthread_mutex.rs
+// 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 {
#[cfg(feature = "alloc")]
@@ -40,8 +42,9 @@ mod pthread_mtx {
#[derive(Debug)]
pub enum Error {
- #[expect(dead_code)]
+ #[allow(dead_code)]
IO(std::io::Error),
+ #[allow(dead_code)]
Alloc,
}
@@ -59,6 +62,7 @@ mod pthread_mtx {
}
impl<T> PThreadMutex<T> {
+ #[allow(dead_code)]
pub fn new(data: T) -> impl PinInit<Self, Error> {
fn init_raw() -> impl PinInit<UnsafeCell<libc::pthread_mutex_t>, Error> {
let init = |slot: *mut UnsafeCell<libc::pthread_mutex_t>| {
@@ -101,6 +105,7 @@ mod pthread_mtx {
}? Error)
}
+ #[allow(dead_code)]
pub fn lock(&self) -> PThreadMutexGuard<'_, T> {
// SAFETY: raw is always initialized
unsafe { libc::pthread_mutex_lock(self.raw.get()) };
@@ -135,6 +140,7 @@ mod pthread_mtx {
}
#[cfg_attr(test, test)]
+#[cfg_attr(all(test, miri), ignore)]
fn main() {
#[cfg(all(any(feature = "std", feature = "alloc"), not(windows)))]
{
diff --git a/rust/pin-init/examples/static_init.rs b/rust/pin-init/examples/static_init.rs
index 3487d761aa26..0e165daa9798 100644
--- a/rust/pin-init/examples/static_init.rs
+++ b/rust/pin-init/examples/static_init.rs
@@ -2,6 +2,8 @@
#![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::{
cell::{Cell, UnsafeCell},
@@ -11,12 +13,13 @@ use core::{
time::Duration,
};
use pin_init::*;
+#[cfg(feature = "std")]
use std::{
sync::Arc,
thread::{sleep, Builder},
};
-#[expect(unused_attributes)]
+#[allow(unused_attributes)]
mod mutex;
use mutex::*;
@@ -81,42 +84,41 @@ unsafe impl PinInit<CMutex<usize>> for CountInit {
pub static COUNT: StaticInit<CMutex<usize>, CountInit> = StaticInit::new(CountInit);
-#[cfg(not(any(feature = "std", feature = "alloc")))]
-fn main() {}
-
-#[cfg(any(feature = "std", feature = "alloc"))]
fn main() {
- let mtx: Pin<Arc<CMutex<usize>>> = Arc::pin_init(CMutex::new(0)).unwrap();
- let mut handles = vec![];
- let thread_count = 20;
- let workload = 1_000;
- for i in 0..thread_count {
- let mtx = mtx.clone();
- handles.push(
- Builder::new()
- .name(format!("worker #{i}"))
- .spawn(move || {
- for _ in 0..workload {
- *COUNT.lock() += 1;
- std::thread::sleep(std::time::Duration::from_millis(10));
- *mtx.lock() += 1;
- std::thread::sleep(std::time::Duration::from_millis(10));
- *COUNT.lock() += 1;
- }
- println!("{i} halfway");
- sleep(Duration::from_millis((i as u64) * 10));
- for _ in 0..workload {
- std::thread::sleep(std::time::Duration::from_millis(10));
- *mtx.lock() += 1;
- }
- println!("{i} finished");
- })
- .expect("should not fail"),
- );
- }
- for h in handles {
- h.join().expect("thread panicked");
+ #[cfg(feature = "std")]
+ {
+ let mtx: Pin<Arc<CMutex<usize>>> = Arc::pin_init(CMutex::new(0)).unwrap();
+ let mut handles = vec![];
+ let thread_count = 20;
+ let workload = 1_000;
+ for i in 0..thread_count {
+ let mtx = mtx.clone();
+ handles.push(
+ Builder::new()
+ .name(format!("worker #{i}"))
+ .spawn(move || {
+ for _ in 0..workload {
+ *COUNT.lock() += 1;
+ std::thread::sleep(std::time::Duration::from_millis(10));
+ *mtx.lock() += 1;
+ std::thread::sleep(std::time::Duration::from_millis(10));
+ *COUNT.lock() += 1;
+ }
+ println!("{i} halfway");
+ sleep(Duration::from_millis((i as u64) * 10));
+ for _ in 0..workload {
+ std::thread::sleep(std::time::Duration::from_millis(10));
+ *mtx.lock() += 1;
+ }
+ println!("{i} finished");
+ })
+ .expect("should not fail"),
+ );
+ }
+ for h in handles {
+ h.join().expect("thread panicked");
+ }
+ println!("{:?}, {:?}", &*mtx.lock(), &*COUNT.lock());
+ assert_eq!(*mtx.lock(), workload * thread_count * 2);
}
- println!("{:?}, {:?}", &*mtx.lock(), &*COUNT.lock());
- assert_eq!(*mtx.lock(), workload * thread_count * 2);
}
diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs
index babe5e878550..297b0129a5bf 100644
--- a/rust/pin-init/internal/src/lib.rs
+++ b/rust/pin-init/internal/src/lib.rs
@@ -22,6 +22,7 @@ use proc_macro::TokenStream;
#[cfg(kernel)]
#[path = "../../../macros/quote.rs"]
#[macro_use]
+#[cfg_attr(not(kernel), rustfmt::skip)]
mod quote;
#[cfg(not(kernel))]
#[macro_use]
@@ -46,3 +47,8 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
pub fn derive_zeroable(input: TokenStream) -> TokenStream {
zeroable::derive(input.into()).into()
}
+
+#[proc_macro_derive(MaybeZeroable)]
+pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream {
+ zeroable::maybe_derive(input.into()).into()
+}
diff --git a/rust/pin-init/internal/src/pinned_drop.rs b/rust/pin-init/internal/src/pinned_drop.rs
index c824dd8b436d..c4ca7a70b726 100644
--- a/rust/pin-init/internal/src/pinned_drop.rs
+++ b/rust/pin-init/internal/src/pinned_drop.rs
@@ -28,8 +28,7 @@ pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream
// Found the end of the generics, this should be `PinnedDrop`.
assert!(
matches!(tt, TokenTree::Ident(i) if i.to_string() == "PinnedDrop"),
- "expected 'PinnedDrop', found: '{:?}'",
- tt
+ "expected 'PinnedDrop', found: '{tt:?}'"
);
pinned_drop_idx = Some(i);
break;
diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/internal/src/zeroable.rs
index acc94008c152..e0ed3998445c 100644
--- a/rust/pin-init/internal/src/zeroable.rs
+++ b/rust/pin-init/internal/src/zeroable.rs
@@ -6,7 +6,14 @@ use proc_macro2 as proc_macro;
use crate::helpers::{parse_generics, Generics};
use proc_macro::{TokenStream, TokenTree};
-pub(crate) fn derive(input: TokenStream) -> TokenStream {
+pub(crate) fn parse_zeroable_derive_input(
+ input: TokenStream,
+) -> (
+ Vec<TokenTree>,
+ Vec<TokenTree>,
+ Vec<TokenTree>,
+ Option<TokenTree>,
+) {
let (
Generics {
impl_generics,
@@ -64,6 +71,11 @@ pub(crate) fn derive(input: TokenStream) -> TokenStream {
if in_generic && !inserted {
new_impl_generics.extend(quote! { : ::pin_init::Zeroable });
}
+ (rest, new_impl_generics, ty_generics, last)
+}
+
+pub(crate) fn derive(input: TokenStream) -> TokenStream {
+ let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input);
quote! {
::pin_init::__derive_zeroable!(
parse_input:
@@ -74,3 +86,16 @@ pub(crate) fn derive(input: TokenStream) -> TokenStream {
);
}
}
+
+pub(crate) fn maybe_derive(input: TokenStream) -> TokenStream {
+ let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input);
+ quote! {
+ ::pin_init::__maybe_derive_zeroable!(
+ parse_input:
+ @sig(#(#rest)*),
+ @impl_generics(#(#new_impl_generics)*),
+ @ty_generics(#(#ty_generics)*),
+ @body(#last),
+ );
+ }
+}
diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs
index 557b5948cddc..90f18e9a2912 100644
--- a/rust/pin-init/src/__internal.rs
+++ b/rust/pin-init/src/__internal.rs
@@ -188,6 +188,7 @@ impl<T> StackInit<T> {
}
#[test]
+#[cfg(feature = "std")]
fn stack_init_reuse() {
use ::std::{borrow::ToOwned, println, string::String};
use core::pin::pin;
diff --git a/rust/pin-init/src/alloc.rs b/rust/pin-init/src/alloc.rs
index e16baa3b434e..5017f57442d8 100644
--- a/rust/pin-init/src/alloc.rs
+++ b/rust/pin-init/src/alloc.rs
@@ -17,11 +17,9 @@ use crate::{
pub extern crate alloc;
-// SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee).
-//
-// In this case we are allowed to use `T: ?Sized`, since all zeros is the `None` variant and there
-// is no problem with a VTABLE pointer being null.
-unsafe impl<T: ?Sized> ZeroableOption for Box<T> {}
+// SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee:
+// <https://doc.rust-lang.org/stable/std/option/index.html#representation>).
+unsafe impl<T> ZeroableOption for Box<T> {}
/// Smart pointer that can initialize memory in-place.
pub trait InPlaceInit<T>: Sized {
diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs
index 05c44514765e..62e013a5cc20 100644
--- a/rust/pin-init/src/lib.rs
+++ b/rust/pin-init/src/lib.rs
@@ -32,6 +32,12 @@
//! will require the `std` feature, because stable compilers have neither `Box` nor `Arc` in no-std
//! mode.
//!
+//! ## Nightly needed for `unsafe-pinned` feature
+//!
+//! This feature enables the `Wrapper` implementation on the unstable `core::pin::UnsafePinned` type.
+//! This requires the [`unsafe_pinned` unstable feature](https://github.com/rust-lang/rust/issues/125735)
+//! and therefore a nightly compiler. Note that this feature is not enabled by default.
+//!
//! # Overview
//!
//! To initialize a `struct` with an in-place constructor you will need two things:
@@ -142,7 +148,7 @@
//! fn new() -> impl PinInit<Self, Error> {
//! try_pin_init!(Self {
//! status <- CMutex::new(0),
-//! buffer: Box::init(pin_init::zeroed())?,
+//! buffer: Box::init(pin_init::init_zeroed())?,
//! }? Error)
//! }
//! }
@@ -241,7 +247,7 @@
//! [`sync`]: https://rust.docs.kernel.org/kernel/sync/index.html
//! [pinning]: https://doc.rust-lang.org/std/pin/index.html
//! [structurally pinned fields]:
-//! https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field
+//! https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning
//! [stack]: crate::stack_pin_init
#![cfg_attr(
kernel,
@@ -269,6 +275,10 @@
#![forbid(missing_docs, unsafe_op_in_unsafe_fn)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
+#![cfg_attr(
+ all(feature = "unsafe-pinned", CONFIG_RUSTC_HAS_UNSAFE_PINNED),
+ feature(unsafe_pinned)
+)]
use core::{
cell::UnsafeCell,
@@ -385,9 +395,10 @@ pub use ::pin_init_internal::pin_data;
/// ```
pub use ::pin_init_internal::pinned_drop;
-/// Derives the [`Zeroable`] trait for the given struct.
+/// Derives the [`Zeroable`] trait for the given `struct` or `union`.
///
-/// This can only be used for structs where every field implements the [`Zeroable`] trait.
+/// This can only be used for `struct`s/`union`s where every field implements the [`Zeroable`]
+/// trait.
///
/// # Examples
///
@@ -396,13 +407,54 @@ pub use ::pin_init_internal::pinned_drop;
///
/// #[derive(Zeroable)]
/// pub struct DriverData {
-/// id: i64,
+/// pub(crate) id: i64,
/// buf_ptr: *mut u8,
/// len: usize,
/// }
/// ```
+///
+/// ```
+/// use pin_init::Zeroable;
+///
+/// #[derive(Zeroable)]
+/// pub union SignCast {
+/// signed: i64,
+/// unsigned: u64,
+/// }
+/// ```
pub use ::pin_init_internal::Zeroable;
+/// Derives the [`Zeroable`] trait for the given `struct` or `union` if all fields implement
+/// [`Zeroable`].
+///
+/// Contrary to the derive macro named [`macro@Zeroable`], this one silently fails when a field
+/// doesn't implement [`Zeroable`].
+///
+/// # Examples
+///
+/// ```
+/// use pin_init::MaybeZeroable;
+///
+/// // implmements `Zeroable`
+/// #[derive(MaybeZeroable)]
+/// pub struct DriverData {
+/// pub(crate) id: i64,
+/// buf_ptr: *mut u8,
+/// len: usize,
+/// }
+///
+/// // does not implmement `Zeroable`
+/// #[derive(MaybeZeroable)]
+/// pub struct DriverData2 {
+/// pub(crate) id: i64,
+/// buf_ptr: *mut u8,
+/// len: usize,
+/// // this field doesn't implement `Zeroable`
+/// other_data: &'static i32,
+/// }
+/// ```
+pub use ::pin_init_internal::MaybeZeroable;
+
/// Initialize and pin a type directly on the stack.
///
/// # Examples
@@ -690,7 +742,7 @@ macro_rules! stack_try_pin_init {
/// - Fields that you want to initialize in-place have to use `<-` instead of `:`.
/// - In front of the initializer you can write `&this in` to have access to a [`NonNull<Self>`]
/// pointer named `this` inside of the initializer.
-/// - Using struct update syntax one can place `..Zeroable::zeroed()` at the very end of the
+/// - Using struct update syntax one can place `..Zeroable::init_zeroed()` at the very end of the
/// struct, this initializes every field with 0 and then runs all initializers specified in the
/// body. This can only be done if [`Zeroable`] is implemented for the struct.
///
@@ -717,7 +769,7 @@ macro_rules! stack_try_pin_init {
/// });
/// let init = pin_init!(Buf {
/// buf: [1; 64],
-/// ..Zeroable::zeroed()
+/// ..Zeroable::init_zeroed()
/// });
/// ```
///
@@ -753,7 +805,7 @@ macro_rules! pin_init {
/// ```rust
/// # #![feature(allocator_api)]
/// # #[path = "../examples/error.rs"] mod error; use error::Error;
-/// use pin_init::{pin_data, try_pin_init, PinInit, InPlaceInit, zeroed};
+/// use pin_init::{pin_data, try_pin_init, PinInit, InPlaceInit, init_zeroed};
///
/// #[pin_data]
/// struct BigBuf {
@@ -765,7 +817,7 @@ macro_rules! pin_init {
/// impl BigBuf {
/// fn new() -> impl PinInit<Self, Error> {
/// try_pin_init!(Self {
-/// big: Box::init(zeroed())?,
+/// big: Box::init(init_zeroed())?,
/// small: [0; 1024 * 1024],
/// ptr: core::ptr::null_mut(),
/// }? Error)
@@ -814,7 +866,7 @@ macro_rules! try_pin_init {
/// # #[path = "../examples/error.rs"] mod error; use error::Error;
/// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*;
/// # use pin_init::InPlaceInit;
-/// use pin_init::{init, Init, zeroed};
+/// use pin_init::{init, Init, init_zeroed};
///
/// struct BigBuf {
/// small: [u8; 1024 * 1024],
@@ -823,7 +875,7 @@ macro_rules! try_pin_init {
/// impl BigBuf {
/// fn new() -> impl Init<Self> {
/// init!(Self {
-/// small <- zeroed(),
+/// small <- init_zeroed(),
/// })
/// }
/// }
@@ -861,7 +913,7 @@ macro_rules! init {
/// # #![feature(allocator_api)]
/// # use core::alloc::AllocError;
/// # use pin_init::InPlaceInit;
-/// use pin_init::{try_init, Init, zeroed};
+/// use pin_init::{try_init, Init, init_zeroed};
///
/// struct BigBuf {
/// big: Box<[u8; 1024 * 1024 * 1024]>,
@@ -871,7 +923,7 @@ macro_rules! init {
/// impl BigBuf {
/// fn new() -> impl Init<Self, AllocError> {
/// try_init!(Self {
-/// big: Box::init(zeroed())?,
+/// big: Box::init(init_zeroed())?,
/// small: [0; 1024 * 1024],
/// }? AllocError)
/// }
@@ -901,7 +953,7 @@ macro_rules! try_init {
/// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is
/// structurally pinned.
///
-/// # Example
+/// # Examples
///
/// This will succeed:
/// ```
@@ -1118,7 +1170,7 @@ pub unsafe trait Init<T: ?Sized, E = Infallible>: PinInit<T, E> {
///
/// ```rust
/// # #![expect(clippy::disallowed_names)]
- /// use pin_init::{init, zeroed, Init};
+ /// use pin_init::{init, init_zeroed, Init};
///
/// struct Foo {
/// buf: [u8; 1_000_000],
@@ -1131,7 +1183,7 @@ pub unsafe trait Init<T: ?Sized, E = Infallible>: PinInit<T, E> {
/// }
///
/// let foo = init!(Foo {
- /// buf <- zeroed()
+ /// buf <- init_zeroed()
/// }).chain(|foo| {
/// foo.setup();
/// Ok(())
@@ -1216,6 +1268,38 @@ pub const unsafe fn init_from_closure<T: ?Sized, E>(
__internal::InitClosure(f, PhantomData)
}
+/// Changes the to be initialized type.
+///
+/// # Safety
+///
+/// - `*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`.
+#[expect(clippy::let_and_return)]
+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)
+ res
+}
+
+/// Changes the to be initialized type.
+///
+/// # Safety
+///
+/// - `*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`.
+#[expect(clippy::let_and_return)]
+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)
+ res
+}
+
/// An initializer that leaves the memory uninitialized.
///
/// The initializer is a no-op. The `slot` memory is not changed.
@@ -1306,20 +1390,44 @@ where
unsafe { pin_init_from_closure(init) }
}
-// SAFETY: Every type can be initialized by-value.
-unsafe impl<T, E> Init<T, E> for T {
- unsafe fn __init(self, slot: *mut T) -> Result<(), E> {
- // SAFETY: TODO.
+// 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 `__pinned_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> {
+ // SAFETY: `slot` is valid for writes by the safety requirements of this function.
unsafe { slot.write(self) };
Ok(())
}
}
-// SAFETY: Every type can be initialized by-value. `__pinned_init` calls `__init`.
-unsafe impl<T, E> PinInit<T, E> for T {
+// 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: when the `__pinned_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> {
- // SAFETY: TODO.
- unsafe { self.__init(slot) }
+ // SAFETY: `slot` is valid for writes by the safety requirements of this function.
+ unsafe { slot.write(self?) };
+ Ok(())
}
}
@@ -1387,7 +1495,45 @@ pub unsafe trait PinnedDrop: __internal::HasPinData {
/// ```rust,ignore
/// let val: Self = unsafe { core::mem::zeroed() };
/// ```
-pub unsafe trait Zeroable {}
+pub unsafe trait Zeroable {
+ /// Create a new zeroed `Self`.
+ ///
+ /// The returned initializer will write `0x00` to every byte of the given `slot`.
+ #[inline]
+ fn init_zeroed() -> impl Init<Self>
+ where
+ Self: Sized,
+ {
+ init_zeroed()
+ }
+
+ /// Create a `Self` consisting of all zeroes.
+ ///
+ /// Whenever a type implements [`Zeroable`], this function should be preferred over
+ /// [`core::mem::zeroed()`] or using `MaybeUninit<T>::zeroed().assume_init()`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use pin_init::{Zeroable, zeroed};
+ ///
+ /// #[derive(Zeroable)]
+ /// struct Point {
+ /// x: u32,
+ /// y: u32,
+ /// }
+ ///
+ /// let point: Point = zeroed();
+ /// assert_eq!(point.x, 0);
+ /// assert_eq!(point.y, 0);
+ /// ```
+ fn zeroed() -> Self
+ where
+ Self: Sized,
+ {
+ zeroed()
+ }
+}
/// Marker trait for types that allow `Option<Self>` to be set to all zeroes in order to write
/// `None` to that location.
@@ -1400,11 +1546,21 @@ pub unsafe trait ZeroableOption {}
// SAFETY: by the safety requirement of `ZeroableOption`, this is valid.
unsafe impl<T: ZeroableOption> Zeroable for Option<T> {}
-/// Create a new zeroed 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`.
#[inline]
-pub fn zeroed<T: Zeroable>() -> impl Init<T> {
+pub fn init_zeroed<T: Zeroable>() -> impl Init<T> {
// SAFETY: Because `T: Zeroable`, all bytes zero is a valid bit pattern for `T`
// and because we write all zeroes, the memory is initialized.
unsafe {
@@ -1415,6 +1571,31 @@ pub fn zeroed<T: Zeroable>() -> impl Init<T> {
}
}
+/// Create a `T` consisting of all zeroes.
+///
+/// Whenever a type implements [`Zeroable`], this function should be preferred over
+/// [`core::mem::zeroed()`] or using `MaybeUninit<T>::zeroed().assume_init()`.
+///
+/// # Examples
+///
+/// ```
+/// use pin_init::{Zeroable, zeroed};
+///
+/// #[derive(Zeroable)]
+/// struct Point {
+/// x: u32,
+/// y: u32,
+/// }
+///
+/// let point: Point = zeroed();
+/// assert_eq!(point.x, 0);
+/// assert_eq!(point.y, 0);
+/// ```
+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() }
+}
+
macro_rules! impl_zeroable {
($($({$($generics:tt)*})? $t:ty, )*) => {
// SAFETY: Safety comments written in the macro invocation.
@@ -1447,12 +1628,11 @@ impl_zeroable! {
{<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).
+ // <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>,
- {<T>} Option<NonNull<T>>,
// SAFETY: `null` pointer is valid.
//
@@ -1481,3 +1661,71 @@ macro_rules! impl_tuple_zeroable {
}
impl_tuple_zeroable!(A, B, C, D, E, F, G, H, I, J);
+
+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, $($rest:ident),* $(,)?}) => {
+ // 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 {}
+ impl_fn_zeroable_option!({$($prefix)*} {$($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 });
+
+/// 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).
+///
+/// This is useful when using wrapper `struct`s like [`UnsafeCell`] or with new-type `struct`s.
+///
+/// # Examples
+///
+/// ```
+/// # use core::cell::UnsafeCell;
+/// # use pin_init::{pin_data, pin_init, Wrapper};
+///
+/// #[pin_data]
+/// struct Foo {}
+///
+/// #[pin_data]
+/// struct Bar {
+/// #[pin]
+/// content: UnsafeCell<Foo>
+/// };
+///
+/// let foo_initializer = pin_init!(Foo{});
+/// let initializer = pin_init!(Bar {
+/// content <- UnsafeCell::pin_init(foo_initializer)
+/// });
+/// ```
+pub trait Wrapper<T> {
+ /// Creates an pin-initializer for a [`Self`] containing `T` from the `value_init` initializer.
+ fn pin_init<E>(value_init: impl PinInit<T, E>) -> impl PinInit<Self, E>;
+}
+
+impl<T> Wrapper<T> for UnsafeCell<T> {
+ 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) }
+ }
+}
+
+impl<T> Wrapper<T> for MaybeUninit<T> {
+ 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) }
+ }
+}
+
+#[cfg(all(feature = "unsafe-pinned", CONFIG_RUSTC_HAS_UNSAFE_PINNED))]
+impl<T> Wrapper<T> for core::pin::UnsafePinned<T> {
+ 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) }
+ }
+}
diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs
index 361623324d5c..9ced630737b8 100644
--- a/rust/pin-init/src/macros.rs
+++ b/rust/pin-init/src/macros.rs
@@ -1030,7 +1030,7 @@ macro_rules! __pin_data {
///
/// This macro has multiple internal call configurations, these are always the very first ident:
/// - nothing: this is the base case and called by the `{try_}{pin_}init!` macros.
-/// - `with_update_parsed`: when the `..Zeroable::zeroed()` syntax has been handled.
+/// - `with_update_parsed`: when the `..Zeroable::init_zeroed()` syntax has been handled.
/// - `init_slot`: recursively creates the code that initializes all fields in `slot`.
/// - `make_initializer`: recursively create the struct initializer that guarantees that every
/// field has been initialized exactly once.
@@ -1059,7 +1059,7 @@ macro_rules! __init_internal {
@data($data, $($use_data)?),
@has_data($has_data, $get_data),
@construct_closure($construct_closure),
- @zeroed(), // Nothing means default behavior.
+ @init_zeroed(), // Nothing means default behavior.
)
};
(
@@ -1074,7 +1074,7 @@ macro_rules! __init_internal {
@has_data($has_data:ident, $get_data:ident),
// `pin_init_from_closure` or `init_from_closure`.
@construct_closure($construct_closure:ident),
- @munch_fields(..Zeroable::zeroed()),
+ @munch_fields(..Zeroable::init_zeroed()),
) => {
$crate::__init_internal!(with_update_parsed:
@this($($this)?),
@@ -1084,7 +1084,7 @@ macro_rules! __init_internal {
@data($data, $($use_data)?),
@has_data($has_data, $get_data),
@construct_closure($construct_closure),
- @zeroed(()), // `()` means zero all fields not mentioned.
+ @init_zeroed(()), // `()` means zero all fields not mentioned.
)
};
(
@@ -1124,7 +1124,7 @@ macro_rules! __init_internal {
@has_data($has_data:ident, $get_data:ident),
// `pin_init_from_closure` or `init_from_closure`.
@construct_closure($construct_closure:ident),
- @zeroed($($init_zeroed:expr)?),
+ @init_zeroed($($init_zeroed:expr)?),
) => {{
// We do not want to allow arbitrary returns, so we declare this type as the `Ok` return
// type and shadow it later when we insert the arbitrary user code. That way there will be
@@ -1196,7 +1196,7 @@ macro_rules! __init_internal {
@data($data:ident),
@slot($slot:ident),
@guards($($guards:ident,)*),
- @munch_fields($(..Zeroable::zeroed())? $(,)?),
+ @munch_fields($(..Zeroable::init_zeroed())? $(,)?),
) => {
// Endpoint of munching, no fields are left. If execution reaches this point, all fields
// have been initialized. Therefore we can now dismiss the guards by forgetting them.
@@ -1300,11 +1300,11 @@ macro_rules! __init_internal {
(make_initializer:
@slot($slot:ident),
@type_name($t:path),
- @munch_fields(..Zeroable::zeroed() $(,)?),
+ @munch_fields(..Zeroable::init_zeroed() $(,)?),
@acc($($acc:tt)*),
) => {
// Endpoint, nothing more to munch, create the initializer. Since the users specified
- // `..Zeroable::zeroed()`, the slot will already have been zeroed and all field that have
+ // `..Zeroable::init_zeroed()`, the slot will already have been zeroed and all field that have
// not been overwritten are thus zero and initialized. We still check that all fields are
// actually accessible by using the struct update syntax ourselves.
// We are inside of a closure that is never executed and thus we can abuse `slot` to
@@ -1393,7 +1393,7 @@ macro_rules! __derive_zeroable {
@body({
$(
$(#[$($field_attr:tt)*])*
- $field:ident : $field_ty:ty
+ $field_vis:vis $field:ident : $field_ty:ty
),* $(,)?
}),
) => {
@@ -1412,4 +1412,93 @@ macro_rules! __derive_zeroable {
}
};
};
+ (parse_input:
+ @sig(
+ $(#[$($struct_attr:tt)*])*
+ $vis:vis union $name:ident
+ $(where $($whr:tt)*)?
+ ),
+ @impl_generics($($impl_generics:tt)*),
+ @ty_generics($($ty_generics:tt)*),
+ @body({
+ $(
+ $(#[$($field_attr:tt)*])*
+ $field_vis:vis $field:ident : $field_ty:ty
+ ),* $(,)?
+ }),
+ ) => {
+ // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
+ #[automatically_derived]
+ unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*>
+ where
+ $($($whr)*)?
+ {}
+ const _: () = {
+ fn assert_zeroable<T: ?::core::marker::Sized + $crate::Zeroable>() {}
+ fn ensure_zeroable<$($impl_generics)*>()
+ where $($($whr)*)?
+ {
+ $(assert_zeroable::<$field_ty>();)*
+ }
+ };
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __maybe_derive_zeroable {
+ (parse_input:
+ @sig(
+ $(#[$($struct_attr:tt)*])*
+ $vis:vis struct $name:ident
+ $(where $($whr:tt)*)?
+ ),
+ @impl_generics($($impl_generics:tt)*),
+ @ty_generics($($ty_generics:tt)*),
+ @body({
+ $(
+ $(#[$($field_attr:tt)*])*
+ $field_vis:vis $field:ident : $field_ty:ty
+ ),* $(,)?
+ }),
+ ) => {
+ // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
+ #[automatically_derived]
+ unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*>
+ where
+ $(
+ // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds`
+ // feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
+ $field_ty: for<'__dummy> $crate::Zeroable,
+ )*
+ $($($whr)*)?
+ {}
+ };
+ (parse_input:
+ @sig(
+ $(#[$($struct_attr:tt)*])*
+ $vis:vis union $name:ident
+ $(where $($whr:tt)*)?
+ ),
+ @impl_generics($($impl_generics:tt)*),
+ @ty_generics($($ty_generics:tt)*),
+ @body({
+ $(
+ $(#[$($field_attr:tt)*])*
+ $field_vis:vis $field:ident : $field_ty:ty
+ ),* $(,)?
+ }),
+ ) => {
+ // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
+ #[automatically_derived]
+ unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*>
+ where
+ $(
+ // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds`
+ // feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
+ $field_ty: for<'__dummy> $crate::Zeroable,
+ )*
+ $($($whr)*)?
+ {}
+ };
}