Skip to content

Commit 1427a27

Browse files
committed
Merge remote-tracking branch 'origin/dev/refs-in-init-macros'
2 parents 1bb42ea + 0fc2b6c commit 1427a27

File tree

8 files changed

+187
-35
lines changed

8 files changed

+187
-35
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- Add initializer code blocks to `[try_][pin_]init!` macros: make initializer
1414
macros accept any number of `_: {/* arbitrary code */},` & make them run the
1515
code at that point.
16+
- Allow users to specify `#[bind]` on field assignments in `[try_][pin_]init!`
17+
macros. Doing so exposes the marked field via a `let` binding as `&mut T` or
18+
`Pin<&mut T>`.
1619

1720
## [0.0.10] - 2025-08-19
1821

src/macros.rs

Lines changed: 115 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,38 +1049,56 @@ macro_rules! __pin_data {
10491049
@pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type:ty),* $(,)?),
10501050
@not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:ty),* $(,)?),
10511051
) => {
1052-
// For every field, we create a projection function according to its projection type. If a
1053-
// field is structurally pinned, then it must be initialized via `PinInit`, if it is not
1054-
// structurally pinned, then it can be initialized via `Init`.
1055-
//
1056-
// The functions are `unsafe` to prevent accidentally calling them.
1057-
#[allow(dead_code)]
1058-
#[expect(clippy::missing_safety_doc)]
1059-
impl<$($impl_generics)*> $pin_data<$($ty_generics)*>
1060-
where $($whr)*
1061-
{
1062-
$(
1063-
$(#[$($p_attr)*])*
1064-
$pvis unsafe fn $p_field<E>(
1065-
self,
1066-
slot: *mut $p_type,
1067-
init: impl $crate::PinInit<$p_type, E>,
1068-
) -> ::core::result::Result<(), E> {
1069-
// SAFETY: TODO.
1070-
unsafe { $crate::PinInit::__pinned_init(init, slot) }
1071-
}
1072-
)*
1073-
$(
1074-
$(#[$($attr)*])*
1075-
$fvis unsafe fn $field<E>(
1076-
self,
1077-
slot: *mut $type,
1078-
init: impl $crate::Init<$type, E>,
1079-
) -> ::core::result::Result<(), E> {
1080-
// SAFETY: TODO.
1081-
unsafe { $crate::Init::__init(init, slot) }
1082-
}
1083-
)*
1052+
$crate::macros::paste! {
1053+
// For every field, we create a projection function according to its projection type. If a
1054+
// field is structurally pinned, then it must be initialized via `PinInit`, if it is not
1055+
// structurally pinned, then it can be initialized via `Init`.
1056+
//
1057+
// The functions are `unsafe` to prevent accidentally calling them.
1058+
#[allow(dead_code)]
1059+
#[expect(clippy::missing_safety_doc)]
1060+
impl<$($impl_generics)*> $pin_data<$($ty_generics)*>
1061+
where $($whr)*
1062+
{
1063+
$(
1064+
$(#[$($p_attr)*])*
1065+
$pvis unsafe fn $p_field<E>(
1066+
self,
1067+
slot: *mut $p_type,
1068+
init: impl $crate::PinInit<$p_type, E>,
1069+
) -> ::core::result::Result<(), E> {
1070+
// SAFETY: TODO.
1071+
unsafe { $crate::PinInit::__pinned_init(init, slot) }
1072+
}
1073+
1074+
$(#[$($p_attr)*])*
1075+
$pvis unsafe fn [<__project_ $p_field>]<'__slot>(
1076+
self,
1077+
slot: &'__slot mut $p_type,
1078+
) -> ::core::pin::Pin<&'__slot mut $p_type> {
1079+
::core::pin::Pin::new_unchecked(slot)
1080+
}
1081+
)*
1082+
$(
1083+
$(#[$($attr)*])*
1084+
$fvis unsafe fn $field<E>(
1085+
self,
1086+
slot: *mut $type,
1087+
init: impl $crate::Init<$type, E>,
1088+
) -> ::core::result::Result<(), E> {
1089+
// SAFETY: TODO.
1090+
unsafe { $crate::Init::__init(init, slot) }
1091+
}
1092+
1093+
$(#[$($attr)*])*
1094+
$fvis unsafe fn [<__project_ $field>]<'__slot>(
1095+
self,
1096+
slot: &'__slot mut $type,
1097+
) -> &'__slot mut $type {
1098+
slot
1099+
}
1100+
)*
1101+
}
10841102
}
10851103
};
10861104
}
@@ -1292,6 +1310,13 @@ macro_rules! __init_internal {
12921310
// return when an error/panic occurs.
12931311
// We also use the `data` to require the correct trait (`Init` or `PinInit`) for `$field`.
12941312
unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), init)? };
1313+
// SAFETY:
1314+
// - the project function does the correct field projection,
1315+
// - the field has been initialized,
1316+
// - the reference is only valid until the end of the initializer.
1317+
#[allow(unused_variables)]
1318+
let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) });
1319+
12951320
// Create the drop guard:
12961321
//
12971322
// We rely on macro hygiene to make it impossible for users to access this local variable.
@@ -1323,6 +1348,14 @@ macro_rules! __init_internal {
13231348
// SAFETY: `slot` is valid, because we are inside of an initializer closure, we
13241349
// return when an error/panic occurs.
13251350
unsafe { $crate::Init::__init(init, ::core::ptr::addr_of_mut!((*$slot).$field))? };
1351+
1352+
// SAFETY:
1353+
// - the field is not structurally pinned, since the line above must compile,
1354+
// - the field has been initialized,
1355+
// - the reference is only valid until the end of the initializer.
1356+
#[allow(unused_variables)]
1357+
let $field = unsafe { &mut (*$slot).$field };
1358+
13261359
// Create the drop guard:
13271360
//
13281361
// We rely on macro hygiene to make it impossible for users to access this local variable.
@@ -1341,7 +1374,48 @@ macro_rules! __init_internal {
13411374
);
13421375
}
13431376
};
1344-
(init_slot($($use_data:ident)?):
1377+
(init_slot(): // No `use_data`, so all fields are not structurally pinned
1378+
@data($data:ident),
1379+
@slot($slot:ident),
1380+
@guards($($guards:ident,)*),
1381+
// Init by-value.
1382+
@munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*),
1383+
) => {
1384+
{
1385+
$(let $field = $val;)?
1386+
// Initialize the field.
1387+
//
1388+
// SAFETY: The memory at `slot` is uninitialized.
1389+
unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
1390+
}
1391+
1392+
#[allow(unused_variables)]
1393+
// SAFETY:
1394+
// - the field is not structurally pinned, since no `use_data` was required to create this
1395+
// initializer,
1396+
// - the field has been initialized,
1397+
// - the reference is only valid until the end of the initializer.
1398+
let $field = unsafe { &mut (*$slot).$field };
1399+
1400+
// Create the drop guard:
1401+
//
1402+
// We rely on macro hygiene to make it impossible for users to access this local variable.
1403+
// We use `paste!` to create new hygiene for `$field`.
1404+
$crate::macros::paste! {
1405+
// SAFETY: We forget the guard later when initialization has succeeded.
1406+
let [< __ $field _guard >] = unsafe {
1407+
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
1408+
};
1409+
1410+
$crate::__init_internal!(init_slot():
1411+
@data($data),
1412+
@slot($slot),
1413+
@guards([< __ $field _guard >], $($guards,)*),
1414+
@munch_fields($($rest)*),
1415+
);
1416+
}
1417+
};
1418+
(init_slot($use_data:ident):
13451419
@data($data:ident),
13461420
@slot($slot:ident),
13471421
@guards($($guards:ident,)*),
@@ -1355,6 +1429,13 @@ macro_rules! __init_internal {
13551429
// SAFETY: The memory at `slot` is uninitialized.
13561430
unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
13571431
}
1432+
// SAFETY:
1433+
// - the project function does the correct field projection,
1434+
// - the field has been initialized,
1435+
// - the reference is only valid until the end of the initializer.
1436+
#[allow(unused_variables)]
1437+
let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) });
1438+
13581439
// Create the drop guard:
13591440
//
13601441
// We rely on macro hygiene to make it impossible for users to access this local variable.
@@ -1365,7 +1446,7 @@ macro_rules! __init_internal {
13651446
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
13661447
};
13671448

1368-
$crate::__init_internal!(init_slot($($use_data)?):
1449+
$crate::__init_internal!(init_slot($use_data):
13691450
@data($data),
13701451
@slot($slot),
13711452
@guards([< __ $field _guard >], $($guards,)*),
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use pin_init::*;
2+
3+
struct Foo {
4+
x: usize,
5+
y: usize,
6+
}
7+
8+
fn main() {
9+
let x = 42;
10+
let _ = init!(Foo { x, y: x });
11+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0308]: mismatched types
2+
--> tests/ui/compile-fail/init/shadowing_field.rs:10:13
3+
|
4+
10 | let _ = init!(Foo { x, y: x });
5+
| ^^^^^^^^^^^^^^^^^^^^^^
6+
| |
7+
| expected `usize`, found `&mut usize`
8+
| arguments to this function are incorrect
9+
|
10+
note: function defined here
11+
--> $RUST/core/src/ptr/mod.rs
12+
|
13+
| pub const unsafe fn write<T>(dst: *mut T, src: T) {
14+
| ^^^^^
15+
= note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)

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-
|5 $($acc)*
15+
|6 $($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
@@ -91,20 +91,38 @@ const _: () = {
9191
) -> ::core::result::Result<(), E> {
9292
unsafe { ::pin_init::PinInit::__pinned_init(init, slot) }
9393
}
94+
unsafe fn __project__pin<'__slot>(
95+
self,
96+
slot: &'__slot mut PhantomPinned,
97+
) -> ::core::pin::Pin<&'__slot mut PhantomPinned> {
98+
::core::pin::Pin::new_unchecked(slot)
99+
}
94100
unsafe fn array<E>(
95101
self,
96102
slot: *mut [u8; 1024 * 1024],
97103
init: impl ::pin_init::Init<[u8; 1024 * 1024], E>,
98104
) -> ::core::result::Result<(), E> {
99105
unsafe { ::pin_init::Init::__init(init, slot) }
100106
}
107+
unsafe fn __project_array<'__slot>(
108+
self,
109+
slot: &'__slot mut [u8; 1024 * 1024],
110+
) -> &'__slot mut [u8; 1024 * 1024] {
111+
slot
112+
}
101113
unsafe fn r<E>(
102114
self,
103115
slot: *mut &'b mut [&'a mut T; SIZE],
104116
init: impl ::pin_init::Init<&'b mut [&'a mut T; SIZE], E>,
105117
) -> ::core::result::Result<(), E> {
106118
unsafe { ::pin_init::Init::__init(init, slot) }
107119
}
120+
unsafe fn __project_r<'__slot>(
121+
self,
122+
slot: &'__slot mut &'b mut [&'a mut T; SIZE],
123+
) -> &'__slot mut &'b mut [&'a mut T; SIZE] {
124+
slot
125+
}
108126
}
109127
unsafe impl<
110128
'a,

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,25 @@ const _: () = {
5050
) -> ::core::result::Result<(), E> {
5151
unsafe { ::pin_init::PinInit::__pinned_init(init, slot) }
5252
}
53+
unsafe fn __project__pin<'__slot>(
54+
self,
55+
slot: &'__slot mut PhantomPinned,
56+
) -> ::core::pin::Pin<&'__slot mut PhantomPinned> {
57+
::core::pin::Pin::new_unchecked(slot)
58+
}
5359
unsafe fn array<E>(
5460
self,
5561
slot: *mut [u8; 1024 * 1024],
5662
init: impl ::pin_init::Init<[u8; 1024 * 1024], E>,
5763
) -> ::core::result::Result<(), E> {
5864
unsafe { ::pin_init::Init::__init(init, slot) }
5965
}
66+
unsafe fn __project_array<'__slot>(
67+
self,
68+
slot: &'__slot mut [u8; 1024 * 1024],
69+
) -> &'__slot mut [u8; 1024 * 1024] {
70+
slot
71+
}
6072
}
6173
unsafe impl ::pin_init::__internal::HasPinData for Foo {
6274
type PinData = __ThePinData;

tests/ui/expand/pinned_drop.expanded.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,25 @@ const _: () = {
5050
) -> ::core::result::Result<(), E> {
5151
unsafe { ::pin_init::PinInit::__pinned_init(init, slot) }
5252
}
53+
unsafe fn __project__pin<'__slot>(
54+
self,
55+
slot: &'__slot mut PhantomPinned,
56+
) -> ::core::pin::Pin<&'__slot mut PhantomPinned> {
57+
::core::pin::Pin::new_unchecked(slot)
58+
}
5359
unsafe fn array<E>(
5460
self,
5561
slot: *mut [u8; 1024 * 1024],
5662
init: impl ::pin_init::Init<[u8; 1024 * 1024], E>,
5763
) -> ::core::result::Result<(), E> {
5864
unsafe { ::pin_init::Init::__init(init, slot) }
5965
}
66+
unsafe fn __project_array<'__slot>(
67+
self,
68+
slot: &'__slot mut [u8; 1024 * 1024],
69+
) -> &'__slot mut [u8; 1024 * 1024] {
70+
slot
71+
}
6072
}
6173
unsafe impl ::pin_init::__internal::HasPinData for Foo {
6274
type PinData = __ThePinData;

0 commit comments

Comments
 (0)