Skip to content

Commit db96c51

Browse files
committed
add references to previously initialized fields
After initializing a field in an initializer macro, create a variable holding a reference that points at that field. The type is either `Pin<&mut T>` or `&mut T` depending on the field's structural pinning kind. Signed-off-by: Benno Lossin <lossin@kernel.org>
1 parent 88e63c3 commit db96c51

File tree

6 files changed

+164
-35
lines changed

6 files changed

+164
-35
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Allow users to specify `#[bind]` on field assignments in `[try_][pin_]init!`
13+
macros. Doing so exposes the marked field via a `let` binding as `&mut T` or
14+
`Pin<&mut T>`.
15+
1016
## [0.0.10] - 2025-08-19
1117

1218
### Added

src/macros.rs

Lines changed: 115 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -988,38 +988,56 @@ macro_rules! __pin_data {
988988
@pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type:ty),* $(,)?),
989989
@not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:ty),* $(,)?),
990990
) => {
991-
// For every field, we create a projection function according to its projection type. If a
992-
// field is structurally pinned, then it must be initialized via `PinInit`, if it is not
993-
// structurally pinned, then it can be initialized via `Init`.
994-
//
995-
// The functions are `unsafe` to prevent accidentally calling them.
996-
#[allow(dead_code)]
997-
#[expect(clippy::missing_safety_doc)]
998-
impl<$($impl_generics)*> $pin_data<$($ty_generics)*>
999-
where $($whr)*
1000-
{
1001-
$(
1002-
$(#[$($p_attr)*])*
1003-
$pvis unsafe fn $p_field<E>(
1004-
self,
1005-
slot: *mut $p_type,
1006-
init: impl $crate::PinInit<$p_type, E>,
1007-
) -> ::core::result::Result<(), E> {
1008-
// SAFETY: TODO.
1009-
unsafe { $crate::PinInit::__pinned_init(init, slot) }
1010-
}
1011-
)*
1012-
$(
1013-
$(#[$($attr)*])*
1014-
$fvis unsafe fn $field<E>(
1015-
self,
1016-
slot: *mut $type,
1017-
init: impl $crate::Init<$type, E>,
1018-
) -> ::core::result::Result<(), E> {
1019-
// SAFETY: TODO.
1020-
unsafe { $crate::Init::__init(init, slot) }
1021-
}
1022-
)*
991+
$crate::macros::paste! {
992+
// For every field, we create a projection function according to its projection type. If a
993+
// field is structurally pinned, then it must be initialized via `PinInit`, if it is not
994+
// structurally pinned, then it can be initialized via `Init`.
995+
//
996+
// The functions are `unsafe` to prevent accidentally calling them.
997+
#[allow(dead_code)]
998+
#[expect(clippy::missing_safety_doc)]
999+
impl<$($impl_generics)*> $pin_data<$($ty_generics)*>
1000+
where $($whr)*
1001+
{
1002+
$(
1003+
$(#[$($p_attr)*])*
1004+
$pvis unsafe fn $p_field<E>(
1005+
self,
1006+
slot: *mut $p_type,
1007+
init: impl $crate::PinInit<$p_type, E>,
1008+
) -> ::core::result::Result<(), E> {
1009+
// SAFETY: TODO.
1010+
unsafe { $crate::PinInit::__pinned_init(init, slot) }
1011+
}
1012+
1013+
$(#[$($p_attr)*])*
1014+
$pvis unsafe fn [<__project_ $p_field>]<'__slot>(
1015+
self,
1016+
slot: &'__slot mut $p_type,
1017+
) -> ::core::pin::Pin<&'__slot mut $p_type> {
1018+
::core::pin::Pin::new_unchecked(slot)
1019+
}
1020+
)*
1021+
$(
1022+
$(#[$($attr)*])*
1023+
$fvis unsafe fn $field<E>(
1024+
self,
1025+
slot: *mut $type,
1026+
init: impl $crate::Init<$type, E>,
1027+
) -> ::core::result::Result<(), E> {
1028+
// SAFETY: TODO.
1029+
unsafe { $crate::Init::__init(init, slot) }
1030+
}
1031+
1032+
$(#[$($attr)*])*
1033+
$fvis unsafe fn [<__project_ $field>]<'__slot>(
1034+
self,
1035+
slot: &'__slot mut $type,
1036+
) -> &'__slot mut $type {
1037+
slot
1038+
}
1039+
)*
1040+
}
10231041
}
10241042
};
10251043
}
@@ -1216,6 +1234,13 @@ macro_rules! __init_internal {
12161234
// return when an error/panic occurs.
12171235
// We also use the `data` to require the correct trait (`Init` or `PinInit`) for `$field`.
12181236
unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), init)? };
1237+
// SAFETY:
1238+
// - the project function does the correct field projection,
1239+
// - the field has been initialized,
1240+
// - the reference is only valid until the end of the initializer.
1241+
#[allow(unused_variables)]
1242+
let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) });
1243+
12191244
// Create the drop guard:
12201245
//
12211246
// We rely on macro hygiene to make it impossible for users to access this local variable.
@@ -1247,6 +1272,14 @@ macro_rules! __init_internal {
12471272
// SAFETY: `slot` is valid, because we are inside of an initializer closure, we
12481273
// return when an error/panic occurs.
12491274
unsafe { $crate::Init::__init(init, ::core::ptr::addr_of_mut!((*$slot).$field))? };
1275+
1276+
// SAFETY:
1277+
// - the field is not structurally pinned, since the line above must compile,
1278+
// - the field has been initialized,
1279+
// - the reference is only valid until the end of the initializer.
1280+
#[allow(unused_variables)]
1281+
let $field = unsafe { &mut (*$slot).$field };
1282+
12501283
// Create the drop guard:
12511284
//
12521285
// We rely on macro hygiene to make it impossible for users to access this local variable.
@@ -1265,7 +1298,48 @@ macro_rules! __init_internal {
12651298
);
12661299
}
12671300
};
1268-
(init_slot($($use_data:ident)?):
1301+
(init_slot(): // No `use_data`, so all fields are not structurally pinned
1302+
@data($data:ident),
1303+
@slot($slot:ident),
1304+
@guards($($guards:ident,)*),
1305+
// Init by-value.
1306+
@munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*),
1307+
) => {
1308+
{
1309+
$(let $field = $val;)?
1310+
// Initialize the field.
1311+
//
1312+
// SAFETY: The memory at `slot` is uninitialized.
1313+
unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
1314+
}
1315+
1316+
#[allow(unused_variables)]
1317+
// SAFETY:
1318+
// - the field is not structurally pinned, since no `use_data` was required to create this
1319+
// initializer,
1320+
// - the field has been initialized,
1321+
// - the reference is only valid until the end of the initializer.
1322+
let $field = unsafe { &mut (*$slot).$field };
1323+
1324+
// Create the drop guard:
1325+
//
1326+
// We rely on macro hygiene to make it impossible for users to access this local variable.
1327+
// We use `paste!` to create new hygiene for `$field`.
1328+
$crate::macros::paste! {
1329+
// SAFETY: We forget the guard later when initialization has succeeded.
1330+
let [< __ $field _guard >] = unsafe {
1331+
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
1332+
};
1333+
1334+
$crate::__init_internal!(init_slot():
1335+
@data($data),
1336+
@slot($slot),
1337+
@guards([< __ $field _guard >], $($guards,)*),
1338+
@munch_fields($($rest)*),
1339+
);
1340+
}
1341+
};
1342+
(init_slot($use_data:ident):
12691343
@data($data:ident),
12701344
@slot($slot:ident),
12711345
@guards($($guards:ident,)*),
@@ -1279,6 +1353,13 @@ macro_rules! __init_internal {
12791353
// SAFETY: The memory at `slot` is uninitialized.
12801354
unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
12811355
}
1356+
// SAFETY:
1357+
// - the project function does the correct field projection,
1358+
// - the field has been initialized,
1359+
// - the reference is only valid until the end of the initializer.
1360+
#[allow(unused_variables)]
1361+
let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) });
1362+
12821363
// Create the drop guard:
12831364
//
12841365
// We rely on macro hygiene to make it impossible for users to access this local variable.
@@ -1289,7 +1370,7 @@ macro_rules! __init_internal {
12891370
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
12901371
};
12911372

1292-
$crate::__init_internal!(init_slot($($use_data)?):
1373+
$crate::__init_internal!(init_slot($use_data):
12931374
@data($data),
12941375
@slot($slot),
12951376
@guards([< __ $field _guard >], $($guards,)*),

tests/ui/compile-fail/init/wrong_generics2.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ help: you might have forgotten to add the struct literal inside the block
1212
--> src/macros.rs
1313
|
1414
~ ::core::ptr::write($slot, $t { SomeStruct {
15-
|9 $($acc)*
15+
|0 $($acc)*
1616
~ } });
1717
|
1818

tests/ui/expand/many_generics.expanded.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,38 @@ const _: () = {
5353
) -> ::core::result::Result<(), E> {
5454
unsafe { ::pin_init::PinInit::__pinned_init(init, slot) }
5555
}
56+
unsafe fn __project__pin<'__slot>(
57+
self,
58+
slot: &'__slot mut PhantomPinned,
59+
) -> ::core::pin::Pin<&'__slot mut PhantomPinned> {
60+
::core::pin::Pin::new_unchecked(slot)
61+
}
5662
unsafe fn array<E>(
5763
self,
5864
slot: *mut [u8; 1024 * 1024],
5965
init: impl ::pin_init::Init<[u8; 1024 * 1024], E>,
6066
) -> ::core::result::Result<(), E> {
6167
unsafe { ::pin_init::Init::__init(init, slot) }
6268
}
69+
unsafe fn __project_array<'__slot>(
70+
self,
71+
slot: &'__slot mut [u8; 1024 * 1024],
72+
) -> &'__slot mut [u8; 1024 * 1024] {
73+
slot
74+
}
6375
unsafe fn r<E>(
6476
self,
6577
slot: *mut &'b mut [&'a mut T; SIZE],
6678
init: impl ::pin_init::Init<&'b mut [&'a mut T; SIZE], E>,
6779
) -> ::core::result::Result<(), E> {
6880
unsafe { ::pin_init::Init::__init(init, slot) }
6981
}
82+
unsafe fn __project_r<'__slot>(
83+
self,
84+
slot: &'__slot mut &'b mut [&'a mut T; SIZE],
85+
) -> &'__slot mut &'b mut [&'a mut T; SIZE] {
86+
slot
87+
}
7088
}
7189
unsafe impl<
7290
'a,

tests/ui/expand/pin-data.expanded.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,25 @@ const _: () = {
2424
) -> ::core::result::Result<(), E> {
2525
unsafe { ::pin_init::PinInit::__pinned_init(init, slot) }
2626
}
27+
unsafe fn __project__pin<'__slot>(
28+
self,
29+
slot: &'__slot mut PhantomPinned,
30+
) -> ::core::pin::Pin<&'__slot mut PhantomPinned> {
31+
::core::pin::Pin::new_unchecked(slot)
32+
}
2733
unsafe fn array<E>(
2834
self,
2935
slot: *mut [u8; 1024 * 1024],
3036
init: impl ::pin_init::Init<[u8; 1024 * 1024], E>,
3137
) -> ::core::result::Result<(), E> {
3238
unsafe { ::pin_init::Init::__init(init, slot) }
3339
}
40+
unsafe fn __project_array<'__slot>(
41+
self,
42+
slot: &'__slot mut [u8; 1024 * 1024],
43+
) -> &'__slot mut [u8; 1024 * 1024] {
44+
slot
45+
}
3446
}
3547
unsafe impl ::pin_init::__internal::HasPinData for Foo {
3648
type PinData = __ThePinData;

tests/ui/expand/pinned_drop.expanded.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,25 @@ const _: () = {
2424
) -> ::core::result::Result<(), E> {
2525
unsafe { ::pin_init::PinInit::__pinned_init(init, slot) }
2626
}
27+
unsafe fn __project__pin<'__slot>(
28+
self,
29+
slot: &'__slot mut PhantomPinned,
30+
) -> ::core::pin::Pin<&'__slot mut PhantomPinned> {
31+
::core::pin::Pin::new_unchecked(slot)
32+
}
2733
unsafe fn array<E>(
2834
self,
2935
slot: *mut [u8; 1024 * 1024],
3036
init: impl ::pin_init::Init<[u8; 1024 * 1024], E>,
3137
) -> ::core::result::Result<(), E> {
3238
unsafe { ::pin_init::Init::__init(init, slot) }
3339
}
40+
unsafe fn __project_array<'__slot>(
41+
self,
42+
slot: &'__slot mut [u8; 1024 * 1024],
43+
) -> &'__slot mut [u8; 1024 * 1024] {
44+
slot
45+
}
3446
}
3547
unsafe impl ::pin_init::__internal::HasPinData for Foo {
3648
type PinData = __ThePinData;

0 commit comments

Comments
 (0)