Skip to content

Commit d5ac5a8

Browse files
authored
Merge pull request #143 from Rust-for-Linux/dev/accessor-rework
Rework initialization and accessor generation
2 parents 5a4877d + aa8b598 commit d5ac5a8

14 files changed

Lines changed: 413 additions & 386 deletions

internal/src/init.rs

Lines changed: 63 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0 OR MIT
22

33
use proc_macro2::{Span, TokenStream};
4-
use quote::{format_ident, quote, quote_spanned};
4+
use quote::{format_ident, quote};
55
use syn::{
66
braced,
77
parse::{End, Parse},
@@ -103,17 +103,15 @@ pub(crate) fn expand(
103103
|(_, err)| Box::new(err),
104104
);
105105
let slot = format_ident!("slot");
106-
let (has_data_trait, data_trait, get_data, init_from_closure) = if pinned {
106+
let (has_data_trait, get_data, init_from_closure) = if pinned {
107107
(
108108
format_ident!("HasPinData"),
109-
format_ident!("PinData"),
110109
format_ident!("__pin_data"),
111110
format_ident!("pin_init_from_closure"),
112111
)
113112
} else {
114113
(
115114
format_ident!("HasInitData"),
116-
format_ident!("InitData"),
117115
format_ident!("__init_data"),
118116
format_ident!("init_from_closure"),
119117
)
@@ -157,8 +155,7 @@ pub(crate) fn expand(
157155
#path::#get_data()
158156
};
159157
// Ensure that `#data` really is of type `#data` and help with type inference:
160-
let init = ::pin_init::__internal::#data_trait::make_closure::<_, #error>(
161-
#data,
158+
let init = #data.__make_closure::<_, #error>(
162159
move |slot| {
163160
#zeroable_check
164161
#this
@@ -231,107 +228,80 @@ fn init_fields(
231228
cfgs.retain(|attr| attr.path().is_ident("cfg"));
232229
cfgs
233230
};
231+
232+
let ident = match kind {
233+
InitializerKind::Value { ident, .. } => ident,
234+
InitializerKind::Init { ident, .. } => ident,
235+
InitializerKind::Code { block, .. } => {
236+
res.extend(quote! {
237+
#(#attrs)*
238+
#[allow(unused_braces)]
239+
#block
240+
});
241+
continue;
242+
}
243+
};
244+
245+
let slot = if pinned {
246+
quote! {
247+
// SAFETY:
248+
// - `slot` is valid and properly aligned.
249+
// - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned.
250+
// - `make_field_check` prevents `#ident` from being used twice, therefore
251+
// `(*slot).#ident` is exclusively accessed and has not been initialized.
252+
(unsafe { #data.#ident(#slot) })
253+
}
254+
} else {
255+
quote! {
256+
// For `init!()` macro, everything is unpinned.
257+
// SAFETY:
258+
// - `&raw mut (*slot).#ident` is valid.
259+
// - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned.
260+
// - `make_field_check` prevents `#ident` from being used twice, therefore
261+
// `(*slot).#ident` is exclusively accessed and has not been initialized.
262+
(unsafe {
263+
::pin_init::__internal::Slot::<::pin_init::__internal::Unpinned, _>::new(
264+
&raw mut (*#slot).#ident
265+
)
266+
})
267+
}
268+
};
269+
270+
// `mixed_site` ensures that the guard is not accessible to the user-controlled code.
271+
let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
272+
234273
let init = match kind {
235274
InitializerKind::Value { ident, value } => {
236-
let mut value_ident = ident.clone();
237-
let value_prep = value.as_ref().map(|value| &value.1).map(|value| {
238-
// Setting the span of `value_ident` to `value`'s span improves error messages
239-
// when the type of `value` is wrong.
240-
value_ident.set_span(value.span());
241-
quote!(let #value_ident = #value;)
242-
});
243-
// Again span for better diagnostics
244-
let write = quote_spanned!(ident.span()=> ::core::ptr::write);
275+
let value = value
276+
.as_ref()
277+
.map(|(_, value)| quote!(#value))
278+
.unwrap_or_else(|| quote!(#ident));
279+
245280
quote! {
246281
#(#attrs)*
247-
{
248-
#value_prep
249-
// SAFETY: TODO
250-
unsafe { #write(&raw mut (*#slot).#ident, #value_ident) };
251-
}
282+
let mut #guard = #slot.write(#value);
283+
252284
}
253285
}
254-
InitializerKind::Init { ident, value, .. } => {
255-
// Again span for better diagnostics
256-
let init = format_ident!("init", span = value.span());
257-
let value_init = if pinned {
258-
quote! {
259-
// SAFETY:
260-
// - `slot` is valid, because we are inside of an initializer closure, we
261-
// return when an error/panic occurs.
262-
// - We also use `#data` to require the correct trait (`Init` or `PinInit`)
263-
// for `#ident`.
264-
unsafe { #data.#ident(&raw mut (*#slot).#ident, #init)? };
265-
}
266-
} else {
267-
quote! {
268-
// SAFETY: `slot` is valid, because we are inside of an initializer
269-
// closure, we return when an error/panic occurs.
270-
unsafe {
271-
::pin_init::Init::__init(
272-
#init,
273-
&raw mut (*#slot).#ident,
274-
)?
275-
};
276-
}
277-
};
286+
InitializerKind::Init { value, .. } => {
278287
quote! {
279288
#(#attrs)*
280-
{
281-
let #init = #value;
282-
#value_init
283-
}
289+
let mut #guard = #slot.init(#value)?;
284290
}
285291
}
286-
InitializerKind::Code { block: value, .. } => quote! {
287-
#(#attrs)*
288-
#[allow(unused_braces)]
289-
#value
290-
},
292+
InitializerKind::Code { .. } => unreachable!(),
291293
};
292-
res.extend(init);
293-
if let Some(ident) = kind.ident() {
294-
// `mixed_site` ensures that the guard is not accessible to the user-controlled code.
295-
let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
296294

297-
// NOTE: The reference is derived from the guard so that it only lives as long as the
298-
// guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident`
299-
// like the unaligned field guard, it will become effectively `'static`.
300-
let accessor = if pinned {
301-
let project_ident = format_ident!("__project_{ident}");
302-
quote! {
303-
// SAFETY: the initialization is pinned.
304-
unsafe { #data.#project_ident(#guard.let_binding()) }
305-
}
306-
} else {
307-
quote! {
308-
#guard.let_binding()
309-
}
310-
};
295+
res.extend(quote! {
296+
#init
311297

312-
res.extend(quote! {
313-
#(#cfgs)*
314-
// Create the drop guard.
315-
//
316-
// SAFETY:
317-
// - `&raw mut (*slot).#ident` is valid.
318-
// - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned.
319-
// - `(*slot).#ident` has been initialized above.
320-
// - We only need the ownership to the pointee back when initialization has
321-
// succeeded, where we `forget` the guard.
322-
let mut #guard = unsafe {
323-
::pin_init::__internal::DropGuard::new(
324-
&raw mut (*slot).#ident
325-
)
326-
};
298+
#(#cfgs)*
299+
#[allow(unused_variables)]
300+
let #ident = #guard.let_binding();
301+
});
327302

328-
#(#cfgs)*
329-
#[allow(unused_variables)]
330-
let #ident = #accessor;
331-
});
332-
guards.push(guard);
333-
guard_attrs.push(cfgs);
334-
}
303+
guards.push(guard);
304+
guard_attrs.push(cfgs);
335305
}
336306
quote! {
337307
#res

internal/src/pin_data.rs

Lines changed: 29 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -352,10 +352,9 @@ fn generate_the_pin_data(
352352
let (impl_generics, ty_generics, whr) = generics.split_for_impl();
353353

354354
// For every field, we create an initializing projection function according to its projection
355-
// type. If a field is structurally pinned, then it must be initialized via `PinInit`, if it is
356-
// not structurally pinned, then it can be initialized via `Init`.
357-
//
358-
// The functions are `unsafe` to prevent accidentally calling them.
355+
// type. If a field is structurally pinned, we create a `Slot` with `Pinned` which must be
356+
// initialized via `PinInit`; if it is not structurally pinned, then we create a `Slot` with
357+
// `Unpinned` which allows initialization via `Init`.
359358
let field_accessors = fields
360359
.iter()
361360
.map(|f| {
@@ -370,57 +369,29 @@ fn generate_the_pin_data(
370369
let field_name = ident
371370
.as_ref()
372371
.expect("only structs with named fields are supported");
373-
let project_ident = format_ident!("__project_{field_name}");
374-
let (init_ty, init_fn, project_ty, project_body, pin_safety) = if f.pinned {
375-
(
376-
quote!(PinInit),
377-
quote!(__pinned_init),
378-
quote!(::core::pin::Pin<&'__slot mut #ty>),
379-
// SAFETY: this field is structurally pinned.
380-
quote!(unsafe { ::core::pin::Pin::new_unchecked(slot) }),
381-
quote!(
382-
/// - `slot` will not move until it is dropped, i.e. it will be pinned.
383-
),
384-
)
372+
let pin_marker = if f.pinned {
373+
quote!(Pinned)
385374
} else {
386-
(
387-
quote!(Init),
388-
quote!(__init),
389-
quote!(&'__slot mut #ty),
390-
quote!(slot),
391-
quote!(),
392-
)
375+
quote!(Unpinned)
393376
};
394-
let slot_safety = format!(
395-
" `slot` points at the field `{field_name}` inside of `{struct_name}`, which is pinned.",
396-
);
397377
quote! {
398378
/// # Safety
399379
///
400-
/// - `slot` is a valid pointer to uninitialized memory.
401-
/// - the caller does not touch `slot` when `Err` is returned, they are only
402-
/// permitted to deallocate.
403-
#pin_safety
404-
#(#attrs)*
405-
#vis unsafe fn #field_name<E>(
406-
self,
407-
slot: *mut #ty,
408-
init: impl ::pin_init::#init_ty<#ty, E>,
409-
) -> ::core::result::Result<(), E> {
410-
// SAFETY: this function has the same safety requirements as the __init function
411-
// called below.
412-
unsafe { ::pin_init::#init_ty::#init_fn(init, slot) }
413-
}
414-
415-
/// # Safety
416-
///
417-
#[doc = #slot_safety]
380+
/// - `slot` is valid and properly aligned.
381+
/// - `(*slot).#field_name` is properly aligned.
382+
/// - `(*slot).#field_name` points to uninitialized and exclusively accessed
383+
/// memory.
418384
#(#attrs)*
419-
#vis unsafe fn #project_ident<'__slot>(
385+
#[inline(always)]
386+
#vis unsafe fn #field_name(
420387
self,
421-
slot: &'__slot mut #ty,
422-
) -> #project_ty {
423-
#project_body
388+
slot: *mut #struct_name #ty_generics,
389+
) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> {
390+
// SAFETY:
391+
// - If `#pin_marker` is `Pinned`, the corresponding field is structurally
392+
// pinned.
393+
// - Other safety requirements follows the safety requirement.
394+
unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#field_name) }
424395
}
425396
}
426397
})
@@ -450,6 +421,16 @@ fn generate_the_pin_data(
450421
impl #impl_generics __ThePinData #ty_generics
451422
#whr
452423
{
424+
/// Type inference helper function.
425+
#[inline(always)]
426+
#vis fn __make_closure<__F, __E>(self, f: __F) -> __F
427+
where
428+
__F: FnOnce(*mut #struct_name #ty_generics) ->
429+
::core::result::Result<::pin_init::__internal::InitOk, __E>,
430+
{
431+
f
432+
}
433+
453434
#field_accessors
454435
}
455436

@@ -464,13 +445,6 @@ fn generate_the_pin_data(
464445
__ThePinData { __phantom: ::pin_init::__internal::PhantomInvariant::new() }
465446
}
466447
}
467-
468-
// SAFETY: TODO
469-
unsafe impl #impl_generics ::pin_init::__internal::PinData for __ThePinData #ty_generics
470-
#whr
471-
{
472-
type Datee = #struct_name #ty_generics;
473-
}
474448
}
475449
}
476450

0 commit comments

Comments
 (0)