Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 63 additions & 93 deletions internal/src/init.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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"),
)
Expand Down Expand Up @@ -157,8 +155,7 @@ pub(crate) fn expand(
#path::#get_data()
};
// Ensure that `#data` really is of type `#data` and help with type inference:
let init = ::pin_init::__internal::#data_trait::make_closure::<_, #error>(
#data,
let init = #data.__make_closure::<_, #error>(
move |slot| {
#zeroable_check
#this
Expand Down Expand Up @@ -231,107 +228,80 @@ fn init_fields(
cfgs.retain(|attr| attr.path().is_ident("cfg"));
cfgs
};

let ident = match kind {
InitializerKind::Value { ident, .. } => ident,
InitializerKind::Init { ident, .. } => ident,
InitializerKind::Code { block, .. } => {
res.extend(quote! {
#(#attrs)*
#[allow(unused_braces)]
#block
});
continue;
}
};

let slot = if pinned {
quote! {
// SAFETY:
// - `slot` is valid and properly aligned.
// - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned.
// - `make_field_check` prevents `#ident` from being used twice, therefore
// `(*slot).#ident` is exclusively accessed and has not been initialized.
(unsafe { #data.#ident(#slot) })
}
} else {
quote! {
// For `init!()` macro, everything is unpinned.
// SAFETY:
// - `&raw mut (*slot).#ident` is valid.
// - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned.
// - `make_field_check` prevents `#ident` from being used twice, therefore
// `(*slot).#ident` is exclusively accessed and has not been initialized.
(unsafe {
::pin_init::__internal::Slot::<::pin_init::__internal::Unpinned, _>::new(
&raw mut (*#slot).#ident
)
})
}
};

// `mixed_site` ensures that the guard is not accessible to the user-controlled code.
let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());

let init = match kind {
InitializerKind::Value { ident, value } => {
let mut value_ident = ident.clone();
let value_prep = value.as_ref().map(|value| &value.1).map(|value| {
// Setting the span of `value_ident` to `value`'s span improves error messages
// when the type of `value` is wrong.
value_ident.set_span(value.span());
quote!(let #value_ident = #value;)
});
// Again span for better diagnostics
let write = quote_spanned!(ident.span()=> ::core::ptr::write);
let value = value
.as_ref()
.map(|(_, value)| quote!(#value))
.unwrap_or_else(|| quote!(#ident));

quote! {
#(#attrs)*
{
#value_prep
// SAFETY: TODO
unsafe { #write(&raw mut (*#slot).#ident, #value_ident) };
}
let mut #guard = #slot.write(#value);

}
}
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,
)?
};
}
};
InitializerKind::Init { value, .. } => {
quote! {
#(#attrs)*
{
let #init = #value;
#value_init
}
let mut #guard = #slot.init(#value)?;
}
}
InitializerKind::Code { block: value, .. } => quote! {
#(#attrs)*
#[allow(unused_braces)]
#value
},
InitializerKind::Code { .. } => unreachable!(),
};
res.extend(init);
if let Some(ident) = kind.ident() {
// `mixed_site` ensures that the guard is not accessible to the user-controlled code.
let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());

// 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! {
#init

res.extend(quote! {
#(#cfgs)*
// Create the drop guard.
//
// 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
)
};
#(#cfgs)*
#[allow(unused_variables)]
let #ident = #guard.let_binding();
});

#(#cfgs)*
#[allow(unused_variables)]
let #ident = #accessor;
});
guards.push(guard);
guard_attrs.push(cfgs);
}
guards.push(guard);
guard_attrs.push(cfgs);
}
quote! {
#res
Expand Down
96 changes: 33 additions & 63 deletions internal/src/pin_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,8 @@ fn generate_unpin_impl(
#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),*
}

Expand Down Expand Up @@ -354,10 +352,9 @@ fn generate_the_pin_data(
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.
// 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(|f| {
Expand All @@ -372,57 +369,29 @@ fn generate_the_pin_data(
let field_name = ident
.as_ref()
.expect("only structs with named fields are supported");
let project_ident = format_ident!("__project_{field_name}");
let (init_ty, init_fn, project_ty, project_body, pin_safety) = if f.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.
),
)
let pin_marker = if f.pinned {
quote!(Pinned)
} else {
(
quote!(Init),
quote!(__init),
quote!(&'__slot mut #ty),
quote!(slot),
quote!(),
)
quote!(Unpinned)
};
let slot_safety = format!(
" `slot` points at the field `{field_name}` inside of `{struct_name}`, 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 #field_name<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]
/// - `slot` is valid and properly aligned.
/// - `(*slot).#field_name` is properly aligned.
/// - `(*slot).#field_name` points to uninitialized and exclusively accessed
/// memory.
#(#attrs)*
#vis unsafe fn #project_ident<'__slot>(
#[inline(always)]
#vis unsafe fn #field_name(
self,
slot: &'__slot mut #ty,
) -> #project_ty {
#project_body
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) }
}
}
})
Expand All @@ -434,9 +403,7 @@ fn generate_the_pin_data(
#vis struct __ThePinData #generics
#whr
{
__phantom: ::core::marker::PhantomData<
fn(#struct_name #ty_generics) -> #struct_name #ty_generics
>,
__phantom: ::pin_init::__internal::PhantomInvariant<#struct_name #ty_generics>,
}

impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics
Expand All @@ -454,6 +421,16 @@ fn generate_the_pin_data(
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
}

Expand All @@ -465,16 +442,9 @@ fn generate_the_pin_data(
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 = #struct_name #ty_generics;
}
}
}

Expand Down
Loading
Loading