mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-06-08 00:07:34 +09:00
pin-init changes for v6.16
Added: - 'Wrapper<T>' trait for creating pin-initializers for wrapper structs with a structurally pinned value such as 'UnsafeCell<T>' or 'MaybeUninit<T>'. - 'MaybeZeroable' derive macro to try to derive 'Zeroable', but not error if not all fields implement it. This is needed to derive 'Zeroable' for all bindgen-generated structs. - 'unsafe fn cast_[pin_]init()' functions to unsafely change the initialized type of an initializer. These are utilized by the 'Wrapper<T>' implementations. Changed: - Added support for visibility in 'Zeroable' derive macro. - Added support for 'union's in 'Zeroable' derive macro. Upstream dev news: - The CI has been streamlined & some bugs with it have been fixed. I also added new workflows to check if the user-space version and the one in the kernel tree have diverged. - I also started to use the issues [1] tab to keep track of any problems or unexpected/unwanted things. This should help folks report and diagnose issues w.r.t. 'pin-init' better. [1]: https://github.com/rust-for-linux/pin-init/issues -----BEGIN PGP SIGNATURE----- iIgEABYKADAWIQQjEG/HT3UeYLJybLTomd21rZaLygUCaBkpnxIcbG9zc2luQGtl cm5lbC5vcmcACgkQ6Jndta2Wi8rvGAEAlmQercahKos8nEIvsP53vckky2OI7NWi /1IjnFqIx0ABAJ4Wm08BDTU3tVOI7hNWIlZbWm0U81+Alk5fgR+Ou30F =ZfOJ -----END PGP SIGNATURE----- Merge tag 'pin-init-v6.16' of https://github.com/Rust-for-Linux/linux into rust-next Pull pin-init updates from Benno Lossin: "Added: - 'Wrapper<T>' trait for creating pin-initializers for wrapper structs with a structurally pinned value such as 'UnsafeCell<T>' or 'MaybeUninit<T>'. - 'MaybeZeroable' derive macro to try to derive 'Zeroable', but not error if not all fields implement it. This is needed to derive 'Zeroable' for all bindgen-generated structs. - 'unsafe fn cast_[pin_]init()' functions to unsafely change the initialized type of an initializer. These are utilized by the 'Wrapper<T>' implementations. Changed: - Added support for visibility in 'Zeroable' derive macro. - Added support for 'union's in 'Zeroable' derive macro. Upstream dev news: - The CI has been streamlined & some bugs with it have been fixed. I also added new workflows to check if the user-space version and the one in the kernel tree have diverged. - I also started to use the issues [1] tab to keep track of any problems or unexpected/unwanted things. This should help folks report and diagnose issues w.r.t. 'pin-init' better. [1] https://github.com/rust-for-linux/pin-init/issues" * tag 'pin-init-v6.16' of https://github.com/Rust-for-Linux/linux: rust: pin-init: improve documentation for `Zeroable` derive macros rust: pin-init: fix typos rust: pin-init: add `MaybeZeroable` derive macro rust: pin-init: allow `Zeroable` derive macro to also be applied to unions rust: pin-init: allow `pub` fields in `derive(Zeroable)` rust: pin-init: Update the structural pinning link in readme. rust: pin-init: Update Changelog and Readme rust: pin-init: Implement `Wrapper` for `UnsafePinned` behind feature flag. rust: pin-init: Add the `Wrapper` trait. rust: pin-init: add `cast_[pin_]init` functions to change the initialized type rust: pin-init: examples: use `allow` instead of `expect` rust: pin-init: examples: conditionally enable `feature(lint_reasons)` rust: pin-init: internal: skip rustfmt formatting of kernel-only module rust: pin-init: synchronize README.md
This commit is contained in:
commit
b04d170621
9 changed files with 279 additions and 10 deletions
|
@ -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:
|
||||
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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::{
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
// 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,7 +42,7 @@ mod pthread_mtx {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
#[expect(dead_code)]
|
||||
#[allow(dead_code)]
|
||||
IO(std::io::Error),
|
||||
Alloc,
|
||||
}
|
||||
|
|
|
@ -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, UnsafeCell},
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -1481,3 +1565,55 @@ macro_rules! impl_tuple_zeroable {
|
|||
}
|
||||
|
||||
impl_tuple_zeroable!(A, B, C, D, E, F, G, H, I, J);
|
||||
|
||||
/// 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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1393,7 +1393,37 @@ macro_rules! __derive_zeroable {
|
|||
@body({
|
||||
$(
|
||||
$(#[$($field_attr:tt)*])*
|
||||
$field:ident : $field_ty:ty
|
||||
$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>();)*
|
||||
}
|
||||
};
|
||||
};
|
||||
(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
|
||||
),* $(,)?
|
||||
}),
|
||||
) => {
|
||||
|
@ -1413,3 +1443,62 @@ macro_rules! __derive_zeroable {
|
|||
};
|
||||
};
|
||||
}
|
||||
|
||||
#[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)*)?
|
||||
{}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue