mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
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:
commit
838a0871cb
|
|
@ -3,7 +3,7 @@
|
|||
[](https://deps.rs/repo/github/Rust-for-Linux/pin-init)
|
||||

|
||||
[](#nightly-only)
|
||||

|
||||

|
||||
# `pin-init`
|
||||
|
||||
> [!NOTE]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user